JAVA快速入门到精通牛客零基础刷题指南:35~37 手把手带刷:常用API,日期工具
常用API
日期工具
Date
Date类用来处理日期和时间,但是该类的大部分构造器、方法均以过时。
- 常用的构造方法
// 创建代表当前时间的Date对象,底层调用System类获取当前时间毫秒数。 public Date() { } // 根据指定的时间毫秒数创建Date对象,参数为时间的毫秒数。 public Date(long date) { }- 常用的成员方法
// 判断该时间是否在指定时间之后 public boolean after(Date when) { } // 判断该时间是否在指定时间之前 public boolean before(Date when) { } // 返回该时间的毫秒数 public long getTime() { } // 以毫秒数的形式,设置该Date对象所代表的时间。 public void setTime(long time) { }Calendar
- 相比于Date类,Calendar类可以更好地处理日期和时间。
- Calendar是一个抽象类,所以不能通过构造器创建Calendar对象。
- Calendar类提供了静态方法getInstance(),用以创建实例。
- Calendar类提供了与Date互转的方法
// 返回Date public final Date getTime() { } // 将Date转为Calendar public final void setTime(Date date) { }- Calendar类常用的成员方法
// 返回指定字段的值 public int get(int field) {} // 给指定的字段设置一个值 public void set(int field, int amount) {} // 设置年月日的值 public void set(int year, int month, int date) {} // 设置年月日时分秒的值 public void set(int year, int month, int date, int hourOfDay, int minute, int second) {}- Calendar类常用的静态变量
YEAR, MONTH, DATE, HOUR, MINITE, SECOND, MILLISECOND
DateFormat
DateFormat用于实现日期的格式化,它是一个抽象类,提供了如下静态方法以创建实例:
// 返回日期格式器 public final static DateFormat getDateInstance() {} // 返回时间格式器 public final static DateFormat getTimeInstance() {} // 返回日期时间格式器 public final static DateFormat getDateTimeInstance() {}同时,DateFormat提供了如下常用的成员方法:
// 将传入的日期格式化为字符串 public final String format(Date date) {} // 将传入的格式字符串解析为日期 public Date parse(String source) throws ParseException {}SimpleDateFormat
SimpleDateFormat是DateFormat的子类,提供了更简单的格式化方案,该类提供了如下常用的构造器,常用的成员方法则与DateFormat一致。
// pattern是一个日期模板字符串,如“yyyy-MM-dd HH:mm:ss” public SimpleDateFormat(String pattern) {}JAVA35 输出某一年的各个月份的天数

Calendar calendar=Calendar.getInstance(); for(int month=1;month<=12;month++){ calendar.set(year,month,0); System.out.println(year+"年"+month+"月:"+calendar.get(calendar.DAY_OF_MONTH)+"天"); }DAY_OF_MONTH 是 java.util.Calendar 类的静态常量(int 类型)
Calendar 的月份是 0 基索引:0 代表 1 月,1 代表 2 月,……,11 代表 12 月,这是 Calendar 最经典的 “坑”
日期传入 0 的特殊规则:Calendar 的set方法中,日期字段传入0,代表取「上一个月的最后一天」。
举个例子帮你理解:
- 当
month=1时,set(year, 1, 0)→ 月份 1 对应日历的 2 月,日期 0 → 自动回退到 2 月的上一个月(1 月)的最后一天。 - 此时调用
get(Calendar.DAY_OF_MONTH),拿到的就是 1 月最后一天的数值,也就是 1 月的总天数。 - 同理,
month=12时,set(year,12,0)→ 月份 12 对应次年 1 月,日期 0 → 回退到当年 12 月的最后一天,拿到 12 月的总天数。
JAVA36 日期换算

//将输入的日期用空格分割成字符串数组 String[] arr=str1.split(" "); //如果不是6位长度,则输入不正常 if(arr.length!=6){ System.out.println("您输入的数据不合理"); } else{ //将日期调整成"yyyy-MM-dd HH:mm:ss"的格式 String str=arr[0]+"-"+arr[1]+"-"+arr[2]+" "+arr[3]+":"+arr[4]+":"+arr[5]; //解析日期 Date date=sdf.parse(str); //输出北京时间 System.out.println("北京时间为:"+sdf.format(date.getTime())); //输出纽约时间 System.out.println("纽约时间为:"+sdf.format(date.getTime()-(long)12*60*60*1000)); }parse是SimpleDateFormat类的核心方法,作用是把符合指定格式的「日期时间字符串」,解析转换为java.util.Date日期对象,是「字符串→日期对象」的转换方法。
format是parse的反向操作,作用是把Date对象 /long类型的时间戳,按照sdf指定的格式模板,转换为人类可读的日期时间字符串。
强转long的 2 个核心原因
原因 1:从根源上规避整数溢出,是行业通用安全规范
如果不加强转,12*60*60*1000的所有操作数都是int,运算结果也是int。虽然 12 小时的毫秒数 43200000,刚好在int范围内,但这是非常脆弱的写法:
- 若后续修改数值(比如计算 30 天、100 天的毫秒数),
30*24*60*60*1000=2592000000,已经超出int最大值,会直接溢出变成负数-1702967296,导致时间计算完全错误。
而把第一个数强转为(long)12后:
- 第一个操作数变成
long类型,后续所有乘法运算都会自动触发类型提升,全程以long类型运算,无论数值多大,都不会发生溢出。 - 这是 Java 处理时间毫秒数的通用最佳实践,因为时间戳本身就是
long类型,加减的数值也必须保证是long类型,从根源上避免 bug。
原因 2:和date.getTime()的long类型匹配,避免隐式转换风险
date.getTime()返回的是long类型(64 位整数),如果减数是int类型,Java 会自动把int提升为long再做减法。
异常处理
- Java语言具有如下的异常处理机制:当程序运行出现意外情形时,系统会自动生成一个异常对象来通知程序。
- 在程序中,我们可以使用特定的语句来捕获异常对象,读取对象中的信息,进而做出处理。
- 也可以使用特定的语句抛出异常对象,将这个异常对象交给程序的调用者处理。
异常的继承体系

- 所有的非正常情况被分为两类:
错误(Error)和异常(Exception),其中Error代表虚拟机相关问题,一般无法处理,也无需处理。
- 所有的 Exception 被分为两类
- RuntimeException,代表运行时异常,程序可以显式处理这种异常,也可以不处理,而是交给顶层调用者统一处理。
- 非运行时异常(Checked异常),程序必须显式地处理这种异常,否则在编译阶段会发生错误,导致程序无法通过编译。
捕获异常
语法:
try { 业务逻辑代码 } catch (AException e) { A异常的处理代码 } catch (BException e) { B异常的处理代码 } ... finally { 回收资源代码 }说明:
1. 无论哪行代码发生异常,系统都会生成一个异常对象,这与try...catch...语句没有关系;
2. 若程序没有对这个异常对象做任何处理,则程序在此退出;
3. 创建异常对象后,JVM会寻找可以处理它的catch块,并将异常对象交给这个catch块去处理;
4. 程序应该先处理小异常、再处理大异常,即将处理父类异常的catch块放在处理子类异常的catch块之后;
5. finally块中代码总是会被执行,它必须位于try、catch之后,通常用来释放资源;
6. try是必须的,catch、finally是可选的,但catch、finally二者之中至少要出现一个。
声明抛出异常
语法:
throws ExceptionClass1, ExceptionClass2, ...说明:
1. throws语句用于标识某方法可能抛出的异常,它必须位于方法签名之后;
2. throws语句声明抛出异常后,程序中旧无需使用try语句捕获该异常了;
3. 在重写时,子类方法声明抛出的异常类型不能比父类方法声明抛出的异常类型大。
抛出异常
语法:
throw ExceptionInstance;说明:
1. throw语句用于在程序中主动抛出一个异常;
2. throw语句抛出的不是异常类型,而是一个异常实例;
3. 对于主动抛出的异常,也可以采用try块捕获,或者采用throws语句向外抛出。
异常的处理原则
1. 不要多度的使用异常:
不要用异常处理代替错误处理代码,不要用异常处理代替流程控制语句;
2. 不要忽略捕获的异常:
对于捕获到的异常,要进行合适的修复,对于不能处理的部分,应该抛出新的异常;
3. 不要直接捕获所有的异常:
应对不同的异常做出有针对性的处理,而捕获所有的异常,容易压制(丢失)异常;
4. 不要使用过于庞大的try块:
庞大的try块会导致业务过于复杂,不利于分析异常的原因,也不利于程序的阅读及维护。
JAVA37 判断学生成绩
import java.util.*; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int score = scanner.nextInt(); try{ //如果考试成绩在0-100之间,直接输出 if(score>=0&&score<=100){ System.out.println(score); } //否则抛异常 else{ throw new ScoreException("分数不合法"); } } //捕获到异常,输出错误信息 catch(ScoreException e){ System.out.println(e.getMessage()); } } } class ScoreException extends Exception{ //利用父类构造方法,初始化信息 ScoreException(String message){ super(message); } } getMessage():获取异常对象中携带的、自定义的异常详细描述文本
- 当分数不合法时,
new ScoreException("分数不合法")会创建一个异常对象,把"分数不合法"通过构造方法,最终存到detailMessage里。 catch块捕获到这个异常对象,赋值给变量str。str.getMessage()会读取异常对象里存储的detailMessage,也就是传入的"分数不合法",最终打印输出。
super(message) 的核心作用:“让父类帮忙初始化数据”
super 在这里的意思是 “父类的”,super(message) 就是 “调用父类中接收一个 String 参数的构造方法”。
public ScoreException(String message){ super(message); // 等价于:调用 Exception(String message) 这个构造方法 }为什么必须写 super(message)?(不写会怎样?)
父类数据的封装性Throwable 里的 detailMessage 是一个 private(私有)变量。子类 ScoreException无权直接修改它。所以必须通过父类提供的 “构造方法入口”(即 super(message))来间接赋值。
如果不写,信息就丢了如果你只写 super()(不传参数),或者干脆不写 super(编译器会自动补一个无参的 super()),那么父类里的 detailMessage 就会是 null。
super 关键字的两种常见用法
用法 | 示例 | 含义 |
|---|---|---|
1. 调用父类构造方法 |
| 在子类构造方法的第一行调用,用于初始化父类的成员变量。 |
2. 调用父类的普通方法/变量 |
| 在子类方法中调用父类被重写(Override)的方法,或者访问父类的成员变量。 |