Java 8 新日期时间 API 详解
一、什么是新的日期时间API?
Java 8 新引入了 java.time 包,该包下包含了 Java 8 中对于日期时间处理的全新解决方案。这是一套全新的日期时间API,旨在解决 Java 8 之前日期时间处理 API 的各种问题。
二、为什么要使用它?
在 Java 8 之前,日期时间处理只能通过 Date、Calendar、SimpleDateFormat 等原生 API 处理,这些 API 存在诸多问题,甚至在某次 Tiago Fernandez 举行的 Java 最烂 API 评选中荣获第二名。

2.1 传统 API 的问题
2.1.1 Date 类的反人类设计
使用 Date 保存日期时,会出现令人困惑的结果:
public static void main(String[] args) {
Date date = new Date(2020, 6, 12);
System.out.println(date);
}
输出结果:

- 年份
2020变成了3920 - 月份
6变成了7
2.1.2 Calendar 类的不一致性
Calendar 类的 MONTH 从 0 开始计数,导致 6 月在 Calendar 中用 5 表示:
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(new StringBuilder("").append(year).append("/").append(month).append("/").append(day));
}
输出结果:

2.1.3 SimpleDateFormat 的线程不安全
SimpleDateFormat 是线程不安全的,在多线程环境下使用需要额外的线程安全处理,增加了开发复杂度。
三、Java 8 新日期时间 API 简介
Java 8 的 java.time 包下的类都具有以下特点:
- 被
final修饰,不可变 - 线程安全
- 设计简洁,易于使用
- 提供了丰富的日期时间操作方法

四、核心类的使用
4.1 Clock - 时钟类
Clock 用于获取指定时区的当前日期和时间,类似于 System.currentTimeMillis()。
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
public class ClockDemo {
public static void main(String[] args) {
Clock clock = Clock.systemUTC(); // 获取当前时钟,使用 UTC 时区
Clock clock2 = Clock.systemDefaultZone(); // 使用系统默认时区
Clock clock3 = Clock.system(ZoneId.of("Asia/Shanghai")); // 使用自定义时区
Clock clock4 = Clock.fixed(Instant.now(), ZoneId.of("Europe/Paris")); // 固定时间的时钟
Clock clock5 = Clock.offset(Clock.systemUTC(), Duration.ofSeconds(2)); // 偏移指定时间的时钟
System.out.println("UTC 时间戳: " + clock.millis());
System.out.println("系统默认时区时间戳: " + clock2.millis());
System.out.println("上海时区时间戳: " + clock3.millis());
System.out.println("巴黎固定时间戳: " + clock4.millis());
System.out.println("UTC+2秒时间戳: " + clock5.millis());
}
}
输出结果:

4.2 Instant - 时间戳
Instant 表示时间戳,精确到纳秒,内部由两个 Long 字段组成:
- 第一部分:1970年1月1日开始到现在的秒数
- 第二部分:纳秒数(永远不会超过 999,999,999)
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
public class InstantDemo {
public static void main(String[] args) {
Instant instant1 = Instant.now(); // 等同于 Instant.now(Clock.systemUTC())
Instant instant2 = Instant.now(Clock.system(ZoneId.of("Asia/Shanghai")));
System.out.println("当前秒数: " + instant1.getEpochSecond());
System.out.println("当前毫秒数: " + instant1.toEpochMilli());
System.out.println("当前纳秒数: " + instant1.getNano());
System.out.println("上海时区秒数: " + instant2.getEpochSecond());
System.out.println("上海时区毫秒数: " + instant2.toEpochMilli());
System.out.println("上海时区纳秒数: " + instant2.getNano());
}
}
4.3 LocalDate, LocalTime, LocalDateTime
LocalDate:不含时间的日期LocalTime:不含日期的时间LocalDateTime:包含日期和时间
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
public class LocalDateTimeDemo {
public static void main(String[] args) {
// 使用系统默认时区创建当前日期时间对象
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println("系统默认时区: " + localDateTime);
// 使用自定义时区创建日期时间对象
localDateTime = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
System.out.println("印度时区: " + localDateTime);
// 使用 LocalDate 和 LocalTime 对象创建日期时间对象
localDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.now());
System.out.println("组合创建: " + localDateTime);
// 使用自定义时间创建日期时间对象
localDateTime = LocalDateTime.of(2020, 6, 12, 12, 12, 12);
System.out.println("自定义时间: " + localDateTime);
// 通过时间戳获取时间
localDateTime = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
System.out.println("时间戳转换: " + localDateTime);
// 日期时间格式化
String date = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("格式化输出: " + date);
// 通过日期字符串创建日期时间对象
localDateTime = LocalDateTime.parse("2020-06-12T12:12:12");
System.out.println("字符串解析: " + localDateTime);
// 获取日期时间各部分
System.out.println("年份: " + localDateTime.getYear());
System.out.println("月份: " + localDateTime.getMonth());
System.out.println("年中的天数: " + localDateTime.getDayOfYear());
System.out.println("月中的天数: " + localDateTime.getDayOfMonth());
System.out.println("星期: " + localDateTime.getDayOfWeek());
System.out.println("时: " + localDateTime.getHour());
System.out.println("分: " + localDateTime.getMinute());
System.out.println("秒: " + localDateTime.getSecond());
System.out.println("纳秒: " + localDateTime.getNano());
}
}
4.4 ZonedDateTime - 带时区的日期时间
ZonedDateTime 包含时区、日期和时间,使用与之前的类类似,但提供了时区支持:
import java.time.ZonedDateTime;
public class ZonedDateTimeDemo {
public static void main(String[] args) {
// 解析带有时区的日期时间字符串
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-06-12T12:12:12Z[Europe/Paris]");
System.out.println("巴黎时间: " + zonedDateTime);
// 创建当前带时区的日期时间
ZonedDateTime now = ZonedDateTime.now();
System.out.println("当前带时区时间: " + now);
// 获取时区信息
System.out.println("时区: " + now.getZone());
}
}
五、日期时间 API 的优势
- 不可变性:所有
java.time包下的类都是不可变的,线程安全 - 清晰的 API 设计:方法名直观,易于理解和使用
- 完整的时区支持:内置了丰富的时区处理功能
- 强大的格式化和解析能力:
DateTimeFormatter提供了灵活的日期时间格式化 - 易于进行日期时间计算:提供了丰富的日期时间调整方法
六、最佳实践
- 优先使用 Java 8 新 API:在新项目中尽量使用
java.time包下的 API - 线程安全考虑:不需要为新 API 添加额外的线程安全处理
- 明确时区:在处理跨时区日期时间时,明确指定时区
- 使用 DateTimeFormatter:替代传统的
SimpleDateFormat - 合理选择类:根据需要选择合适的日期时间类(LocalDate, LocalTime, LocalDateTime, ZonedDateTime)
七、总结
Java 8 的新日期时间 API 解决了传统日期时间 API 的诸多问题,提供了一套设计合理、易于使用、线程安全的日期时间处理方案。通过学习和使用这些新 API,可以大大提高日期时间处理的效率和代码质量。
主要优势包括:
- 清晰的 API 设计,易于理解和使用
- 线程安全,无需额外同步
- 完整的时区支持
- 强大的格式化和解析能力
- 丰富的日期时间计算方法
在日常开发中,我们应该优先使用 Java 8 的新日期时间 API,以获得更好的开发体验和代码质量。
本文深入探讨了Java8中新的日期时间API,包括java.time包下的关键类如Clock、Instant、LocalDate、LocalTime、LocalDateTime、ZonedDateTime的使用方法,解决了旧API存在的问题,如线程安全性和精度不足。

1051

被折叠的 条评论
为什么被折叠?



