目录
时间类
Date
SimpleDateFormat
JDK8新增日期类
异常类
异常概述
异常处理方式
自定义异常
时间
生活中的时间
每个国家的时间, 没有一个统一的标准, 都自己算自己的, 行不行?
答案是不行的
世界标准时间
格林尼治时间/格林威治时间 (Greenwich Mean Time), 简称GMT
是有误差的, 目前已经不作为依据
原子钟
两年万年误差一秒钟
我国的标准时间
北京时间, 要在世界标准时间+8小时 (我们在东8区)
时间换算公式
1秒 = 1000毫秒 (常用)
1毫秒 = 1000微妙
1微妙 = 1000纳秒
计算机中的时间原点 (也可以理解为起始时间)
1970年1月1月 00:00:00
原因
1960年8月, 贝尔实验室的程序员使用B语言在老旧的PDP-7机器上开发出了Unix的一个版本
随后改进了B语言, 开发了C语言, 重写了UNIX
所以认为1970年1月1日是C语言的生日, Java延续了这一传统
以下代码获得的毫秒值, 就是从1970年1月1日到代码运行时经过的毫秒值
long time = System.currentTimeMillis();
System.out.println(time); //1644254175148
Date
Date代表了一个特定的时间, 精确到毫秒
构造方法 (注意目前使用util包下的Date)
public Date(); 创建Date对象, 表示默认(系统)时间
public Date(long date); 创建一个Date对象, 表示指定时间//public Date(); 创建Date对象, 表示默认(系统)时间 Date d1 = new Date(); System.out.println(d1); //Tue Feb 08 16:08:50 CST 2022 //public Date(long date); 创建一个Date对象, 表示指定时间 Date d2 = new Date(0L); System.out.println(d2); //Thu Jan 01 08:00:00 CST 1970 (有8小时时差) //表示1970年1月1日 上午9点 Date d3 = new Date(3600 * 1000L); System.out.println(d3); //Thu Jan 01 09:00:00 CST 1970
Date成员方法
public long getTime(); 获取时间对象的毫秒值
public void setTime(long time); 设置时间, 传递毫秒值//public long getTime(); 获取时间对象的毫秒值 Date d1 = new Date(); //获取时间对象表示的毫秒值 long t1 = d1.getTime(); System.out.println(t1); //1644308106562 //应该和系统时间表示的毫秒值相同 long t2 = System.currentTimeMillis(); System.out.println(t2); //1644308106562 //public void setTime(long time); 设置时间, 传递毫秒值 //简单理解为修改时间 Date d2 = new Date(); //设置为时间原点 (有8小时时差) d2.setTime(0L); System.out.println(d2); //Thu Jan 01 08:00:00 CST 1970
时间日期类-SimpleDateFormat
SimpleDateFormat可以对Date对象, 进行格式化和解析
格式化: Date对象 -> 2020年1月1日 00:00:00
解析: "2020年1月1日 00:00:00" -> Date对象
API中字母及对应关系如下
y 年
M 月
d 日
H 时
m 分
s 秒
常见格式示例
2020-11-11 13:27:06 -> yyyy-MM-dd HH:mm:ss
2020年11月11日 13:27:06 -> yyyy年MM月dd日 HH:mm:ss
SimpleDateFormat构造方法
public SimpleDateFormat(); 构造一个SimpleDateFormat对象, 使用默认格式
public SimpleDateFormat(String 格式); 构造一个SimpleDateFormat对象,使用指定格式
SimpleDateFormat格式化和解析方法
public final String format(Date date); 格式化为指定格式
public Date parse(String source); 从指定格式解析为日期对象
// //public SimpleDateFormat(); 构造一个SimpleDateFormat对象, 使用默认格式
public class Demo01 {
public static void main(String[] args) {
//SimpleDateFormat sdf = new SimpleDateFormat(); //使用默认格式
//SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
//格式化时间, 使用默认格式
String format = sdf.format(new Date());
System.out.println(format);
/*
指定不同的格式进行格式化
2022/2/8 下午4:40
2022-02-08 16:42:37
2022年02月08日
*/
}
}
// public Date parse(String source); 从指定格式解析为日期对象
public class Demo02 {
public static void main(String[] args) throws ParseException {
//提供解析文本
String s = "2022年1月1日 11:11";
//public Date parse(String source); 从指定格式解析为日期对象
//SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd日 HH:mm");
/*
如果指定的格式和要解析的格式不一样, 报错:
java.text.ParseException
*/
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm");
Date date = sdf.parse(s);
System.out.println(date); //Sat Jan 01 11:11:00 CST 2022
}
}
练习
/*
开始时间 2020年11月11日 0:0:0
结束时间 2020年11月11日 0:10:0
张三 2020年11月11日 0:03:47 (秒杀成功)
李四 2020年11月11日 0:10:11 (秒杀失败)
时间节点
*/
public class Demo {
public static void main(String[] args) throws ParseException {
//解析格式相同
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
//解析开始和结束时间, 并获取毫秒值
long startTime = sdf.parse("2020年11月11日 0:0:0").getTime();
long endTime = sdf.parse("2020年11月11日 0:10:0").getTime();
//解析张三和李四的时间, 并获取毫秒值
long zhangsan = sdf.parse("2020年11月11日 0:03:47").getTime();
long lisi = sdf.parse(" 2020年11月11日 0:10:11").getTime();
//判断
if(zhangsan >= startTime && zhangsan <= endTime){
System.out.println("张三同学秒杀成功!");
}else {
System.out.println("张三同学秒杀失败!");
}
if(lisi >= startTime && lisi <= endTime){
System.out.println("李四同学秒杀成功!");
}else {
System.out.println("李四同学秒杀失败!");
}
}
}
JDK8时间类 - LocalDateTime
在JDK8中, 将Date对象进行了拆分
1. LocalDate: 表示日期 (年月日)
2. LocalTime: 表示时间 (时分秒)
3. LocalDateTime: 表示时间+日期 (年月日时分秒)
//public static LocalDateTime now(); 获取当前系统时间
LocalDateTime date1 = LocalDateTime.now();
System.out.println(date1); //2022-02-08T18:19:10.196345700
//public static LocalDateTime of(年,月,日,时,分,秒); 按照参数初始化LocalDateTime对象
//LocalDateTime date2 = LocalDateTime.of(2022, 15, 2, 22, 22, 22);
/*
月份超出了规定范围, 报错:
java.time.DateTimeException: Invalid value for MonthOfYear (valid values 1 - 12): 15
*/
LocalDateTime date2 = LocalDateTime.of(2022, 2, 2, 22, 22, 22);
System.out.println(date2); //2022-02-02T22:22:22
LocalDateTime获取方法
public int getYear(); 获取年
public int getMonthValue(); 获取月份(1-12)
* Month getMonth(); 获取月份返回值为枚举
public int getDayOfMonth(); 获取月份中的第几天(1-31)
public int getDayOfYear(); 获取一年中的第几天(1-366)
public DayOfWeek getDayOfWeek(); 获取星期几
public int getMinute(); 获取分钟
public int getHour(); 获取小时
//创建时间对象
LocalDateTime date = LocalDateTime.of(2022, 11, 11, 11, 11, 11);
//public int getYear(); 获取年
System.out.println(date.getYear()); //2022
//public int getMonthValue(); 获取月份(1-12)
System.out.println(date.getMonthValue()); //11
//* Month getMonth(); 获取月份返回值为枚举
System.out.println(date.getMonth()); //NOVEMBER
//public int getDayOfMonth(); 获取月份中的第几天(1-31)
System.out.println(date.getDayOfMonth()); //11
//public int getDayOfYear(); 获取一年中的第几天(1-366)
System.out.println(date.getDayOfYear()); //315
//public DayOfWeek getDayOfWeek(); 获取星期
System.out.println(date.getDayOfWeek()); //FRIDAY
//public int getMinute(); 获取分钟
System.out.println(date.getMinute()); //11
//public int getHour(); 获取小时
System.out.println(date.getHour()); //11
JDK8时间类-转换方法
从LocalDateTime对象转为LocalDate和LocalTime
public LocalDate toLocalDate();
public LocalTime toLocalTime();
//创建时间对象
LocalDateTime date = LocalDateTime.of(2022, 2, 2, 22, 22, 22);
//public LocalDate toLocalDate();
System.out.println(date.toLocalDate()); //2022-02-02
//public LocalTime toLocalTime();
System.out.println(date.toLocalTime()); //22:22:22
JDK8时间类-格式化和解析
格式化和解析方法
public String format(指定格式); 将LocalDateTime格式化为字符串
public LocalDateTime(准备解析的字符串,解析格式); 解析指定的字符串为LocalDateTime对象
public static DateTimeFormatter ofPattern(String pattern); 获取日期格式化器(JDK8)
JDK8时间类-plus系列的方法
LocalDateTime增加和减少时间的方法
public LocalDateTime plusYears(long years); 增加或减去年
public LocalDateTime plusMonths(long months); 增加或减去月
public LocalDateTime plusDays(long days); 增加或减去日
public LocalDateTime plusHours(long hours); 增加或减去时
public LocalDateTime plusMinutes(long minutes); 增加或减去分
public LocalDateTime plusSeconds(long seconds); 增加或减去秒
public LocalDateTime plusWeeks(long weeks); 增加或减去周
注意事项 : 传递正数是增加, 传递负数是减去
JDK8时间类-minus系列的方法(和plus系列相反 )
public LocalDateTime minusYears(long years); 增加或减去年
public LocalDateTime minusMonths(long months); 增加或减去月
public LocalDateTime minusDays(long days); 增加或减去日
public LocalDateTime minusHours(long hours); 增加或减去时
public LocalDateTime minusMinutes(long minutes); 增加或减去分
public LocalDateTime minusSeconds(long seconds); 增加或减去秒
public LocalDateTime minusWeeks(long weeks); 增加或减去周
JDK8时间类-with系列的方法
public LocalDateTime withYears(long years); 直接修改年
public LocalDateTime withMonths(long months); 直接修改月
public LocalDateTime withDays(long days); 直接修改日
public LocalDateTime withHours(long hours); 直接修改时
public LocalDateTime withMinutes(long minutes); 直接修改分
public LocalDateTime withSeconds(long seconds); 直接修改秒
public LocalDateTime withWeeks(long weeks); 直接修改星期
JDK8时间类-时间间隔对象
时间间隔对象Period(年月日)
public static Period between(开始时间,结束时间); 计算两个"时间"的间隔, 包含年月日
getYears(); 获取年
getMonths(); 获取月
getDays(); 获取日
//创建时间日期对象
LocalDate date1 = LocalDate.of(2022, 1, 1);
LocalDate date2 = LocalDate.of(2023, 12, 12);
//获取时间间隔对象
Period between = Period.between(date1, date2);
System.out.println(between);
/*
P1Y11M11D
P: 时间间隔对象Period
1Y: 1年
11M: 11个月
11D: 11天
*/
//获取年月日
System.out.println(between.getYears()); //1
System.out.println(between.getMonths()); //11
System.out.println(between.getDays()); //11
//获取总月数
System.out.println(between.toTotalMonths()); //23
时间间隔对象Duration(秒,毫秒,纳秒)
public static Duration between(开始时间,结束时间); 计算两个"时间"的间隔, 包含秒,毫秒,纳秒
toSeconds(); 获取秒
toMillis(); 获取毫秒
toNanos(); 获取纳秒
//创建时间对象
LocalDateTime date1 = LocalDateTime.of(2022, 1, 1, 12, 0, 0);
LocalDateTime date2 = LocalDateTime.of(2023, 12, 12, 12, 30, 30);
//获取时间间隔对象
Duration duration = Duration.between(date1, date2);
System.out.println(duration);
/*
PT17040H30M30S
PT: 时间间隔对象
17040H: 17040个小时
30M: 30分钟
30S: 30秒
*/
//获取秒,毫秒,纳秒
System.out.println(duration.toSeconds()); //61345830
System.out.println(duration.toMillis()); //61345830000
System.out.println(duration.toNanos()); //61345830000000000
小结:
LocalDate: 表示日期 (年月日)
LocalTime: 表示时间 (时分秒)
LocalDateTime: 表示时间+日期 (年月日时分秒)LocalDateTime -转-> LocalDate -使用-> toLocalDate();方法
LocalDateTime -转-> LocalTime -使用-> toLocalTime();方法创建时间对象 -> now(); 获取(当前系统)时间对象
-> of(); 获取(指定)时间对象获取时间对象中的,年,月,日,时,分,秒
格式化 -> format
解析 -> parse
增加或减少时间的方法 -> plus开头,minus开头
修改时间 -> with开头时间间隔 -> Period操作年月日
-> Duration操作秒,毫秒,纳秒
异常
什么是异常?
异常就是用来描述代码中出现的问题
注意!
语法错误不算在异常体系中
异常体系?
- Throwable
- Error: 严重错误, 比如内存溢出
- Exception: 异常类, 表示程序本身可以处理的问题
- RuntimeException: 运行时异常(例如空指针异常,索引越界异常)
- 除RuntimeException外所有异常: 编译时异常(编译期必须处理的异常,否则通不过编译,例如格式化异常)
int[] arr = {1,2,3};
//System.out.println(arr[3]); //java.lang.ArrayIndexOutOfBoundsException
arr = null;
//System.out.println(arr[0]); //java.lang.NullPointerException
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
//sdf.parse("2020-1月1日"); //java.text.ParseException
虚拟机默认处理异常的方式
控制台为什么有这样的红色字体, 是谁打印的?
- 当代码出现了异常,在出现异常处会创建异常对象
- 首先看程序有没有自己处理异常的代码(没有)
- 交给本方法的调用者处理(main方法的调用者是JVM, 最终交给JVM处理该异常)
- JVM处理异常默认做了
1. 将异常信息以红色字体提示在控制台上
2. 在出现异常处停止程序运行,后面代码就不执行了
throws-声明异常
异常处理方式1: throws : throws 异常类名
注意: 写在方法的定义处, 表示声明一个异常 (调用我有可能出现这个异常
public class Demo {
public static void main(String[] args) throws ParseException {
//2. 此时调用者没有处理,还是交给JVM处理
//method01(); //java.lang.NullPointerException
//4. throws ParseException: 表示调用者也没有处理,最终还是交给JVM做默认处理
method02(); //java.text.ParseException
}
//3. throws ParseException: 表示该方法有可能出现异常,交给调用者处理
private static void method02() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
sdf.parse("2020-1月1日");
}
//1. throws NullPointerException: 表示该方法有可能出现异常,交给调用者处理
private static void method01() throws NullPointerException {
int[] arr = null;
System.out.println(arr[0]); //java.lang.NullPointerException
}
}
声明异常的注意事项
跟进NullPointerException发现它继承自RuntimeException, 所以是一个运行时异常
跟进ParseException发现它继承自Exception, 所以是一个编译时异常
声明异常的注意事项
如果是运行时异常:可以省略不写 -> method01省略throws NullPointerException没有报错
如果是编译时异常:必须显示声明, 否则代码继续报错 -> method02省略throws ParseException报错
public class Demo {
public static void main(String[] args) throws ParseException {
method01();
method02();
}
// 编译时异常不能省略不写
private static void method02() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
sdf.parse("2020-1月1日");
}
private static void method01(){
int[] arr = null;
System.out.println(arr[0]); //java.lang.NullPointerException
}
}
throw-抛出异常
思考?
以前出现了异常, 虚拟机帮我们创建一个异常对象, 抛给调用者
但是如果我们需要自己手动创建一个异常对象, 该如何实现?
异常处理方式2: throw
throw new 异常();
注意: 这个格式是在方法内的, 表示当前代码手动抛出一个异常, 下面的代码就不再执行了
throws和throw的区别
1. throws: 用在方法声明后, 跟的是异常类名, 表示声明异常, 调用该方法有可能出现该异常
2. throw: 用在方法体内, 跟的是异常对象名(new出来), 表示手动抛出异常, 由方法体内的语句处理
public class Demo01 {
public static void main(String[] args) {
System.out.println("家里有一个貌美如花的老婆");
System.out.println("还有一个当官的兄弟");
System.out.println("自己还有一个买卖");
System.out.println("这样的生活你要不要?");
/*
手动抛出(创建)异常,暂时交给调用者处理
下面的代码就不会执行了
*/
throw new RuntimeException();
//System.out.println("武大郎的标准生活");
}
}
为什么要抛出异常? 因为要将执行结果给调用者进行反馈
public class Demo02 {
public static void main(String[] args) {
int[] arr = null;
printArr(arr);
//4. 调用者如果不处理, 还是抛给了JVM做默认处理
//5. 控制台打印异常信息: java.lang.NullPointerException
//6. 接下来学习, 如果处理异常!
}
public static void printArr(int[] arr){
if(arr== null){
//1. 问题: 调用者知道成功打印了吗?
//2. 回答: 不知道, 因为方法只是将结果打印在控制台,并没有反馈一个结果给调用者
//System.out.println("参数不能为null");
//3. 代码改进: 手动抛出异常, 抛给调用者, 调用者就得到回馈了
throw new NullPointerException();
}else {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
}
try...catch自己处理异常
异常处理方式3: try...catch
try{
可能出现异常的代码;
}catch(异常类名 变量名){
处理异常的代码;
}
快捷键: Ctrl + Alt + T, 选择对应操作, 修改对应的代码
public static void main(String[] args) {
int[] arr = null;
//2. 手动处理异常
try {
printArr(arr);//可能出现异常的代码
} catch (NullPointerException e) {
System.out.println("参数不能为null"); //处理异常的代码
}
//3. 该方式的优点: 让可能出现异常的后面, 正确代码可以继续运行!
System.out.println("看看我们能不能执行...");
}
public static void printArr(int[] arr){
if(arr== null){
//1. 抛出异常给调用者处理
throw new NullPointerException();
}else {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
try...catch的常见问题
try...catch的常见问题
1. 如果try中没有遇到问题, 怎么执行?
会将try中所有代码执行完毕, 不会执行catch中的代码
继续执行后面的正确代码
2. 如果try中遇到了问题, 那么try下面的代码还会执行吗?
try中哪里遇到问题在哪里停下, 直接进入对应的catch执行代码
继续执行后面的正确代码
3. 如果出现的问题没有被捕获, 那么程序如何运行?
try...catch相当于没有写, 异常交给调用者处理
JVM进行默认处理方式(控制台报错)
4. 同时有可能出现多个异常怎么处理?
书写多个catch捕获可能出现的不同异常即可
注意: 如果多个异常出现子父类关系, 父类要写在下面
原因: 代码会按照catch的书写顺序排错, 如果父类在上面, 就可以接收到子类异常了
问题? 能不能只写一个catch直接捕获父类异常Exception
不建议! 因为针对不同异常, 我们通常解决方案是不一样的!
try {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的年龄:");
String line = sc.nextLine();
//第一个异常
int age = Integer.parseInt(line);
System.out.println(age);
//第二个异常
System.out.println(2 / 0);
} catch (NumberFormatException e) {
System.out.println("格式化异常..."); //catch可以写多个, 捕获不同的异常
} catch (ArithmeticException e) {
System.out.println("数学运算异常..."); //catch可以写多个, 捕获不同的异常
}
System.out.println("我是try...catch后面的代码...");
try {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的年龄:");
String line = sc.nextLine(); //输入错误数据: aaa
int age = Integer.parseInt(line);
System.out.println(age);
System.out.println("我是try中的代码..."); //不执行
} catch (NullPointerException e) {
//出现的问题没有被捕获, try...catch相当于没有写, JVM进行默认处理方法
System.out.println("我是catch中的代码..."); //不执行
}
System.out.println("我是try...catch后面的代码..."); //不执行
try {
Scanner sc= new Scanner(System.in);
System.out.println("请输入你的年龄:");
String line = sc.nextLine(); //输入错误数据: aaa
int age = Integer.parseInt(line);
System.out.println(age);
System.out.println("我是try中的代码..."); //不执行
} catch (NumberFormatException e) {
System.out.println("我是catch中的代码..."); //执行
}
System.out.println("我是try...catch后面的代码..."); //执行
try {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的年龄:");
String line = sc.nextLine(); //输入正确数据: 18
int age = Integer.parseInt(line);
System.out.println(age);
System.out.println("我是try中的代码..."); //执行
} catch (NumberFormatException e) {
System.out.println("我是catch中的代码...");
}
System.out.println("我是try...catch后面的代码..."); //执行
}
throwable成员方法
throwable成员方法
1. public String getMessage(); 返回详细消息字符串
2. public String toString(); 返回简短描述
3. public void printStackTrace(); 将异常错误信息打印在控制台(红色字体,注意这里的JVM默认处理方式不一样!)
public static void main(String[] args) {
try {
int[] arr = {1,2,3};
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException e) {
//1. public String getMessage(); 返回详细消息字符串
//String message = e.getMessage();
//System.out.println(message); //Index 3 out of bounds for length 3
//2. public String toString(); 返回简短描述
//String s = e.toString();
//System.out.println(s);
//java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
//3. public void printStackTrace();
//将异常错误信息打印在控制台(红色字体,注意这里的JVM默认处理方式不一样!)
e.printStackTrace();
}
System.out.println("看我执行了吗?如果我执行了,那么就和JVM默认的处理方式不同哟!");
}
异常的小练习
需求:
键盘录入学生姓名和年龄,年龄范围是18-25
超出这个范围的异常数据不能赋值,需要重新录入,直到正确为止//学生类 public class Student { ... public void setAge(int age) { if (age >= 18 && age <= 25) { this.age = age; } else { //如果年龄错误,抛出运行时异常 throw new RuntimeException("年龄超出了范围!"); } } ... } //测试类 public class Demo { public static void main(String[] args) { //创建学生和键盘对象 Student stu = new Student(); Scanner sc = new Scanner(System.in); //提示录入姓名,接收并赋值 System.out.println("请输入姓名:"); String name = sc.nextLine(); stu.setName(name); //循环录入年龄,知道正确为止 while (true) { System.out.println("请输入年龄:"); String line = sc.nextLine(); try { //有可能出现异常的代码 int age = Integer.parseInt(line); stu.setAge(age); //死循环出口 break; } catch (NumberFormatException e) { //捕获转换异常 System.out.println("请输入一个整数"); } catch (RuntimeException e) { //捕获set方法抛出得异常 System.out.println("请输入一个合法的年龄"); } } System.out.println("信息录入成功,程序结束!"); } }
自定义异常
自定义异常的步骤
1. 定义异常类, 类名见名知意
2. 继承关系, 一般继承运行时异常
3. 提供空参和带参构造
代码示例
public class AgeOutOfBoundsException extends RuntimeException{
public AgeOutOfBoundsException() {
}
public AgeOutOfBoundsException(String message) {
super(message);
}
}