Java中的Java 8新特性:日期时间API详解
字数 1945 2025-12-05 23:52:11
Java中的Java 8新特性:日期时间API详解
Java 8 引入了一套全新的日期时间 API (java.time 包) 以彻底解决旧的 java.util.Date 和 java.util.Calendar 类的缺陷。本知识点将详细介绍其核心概念、主要类及其使用方法。
一、 旧日期时间 API 的问题
旧的日期时间 API (Date, Calendar, SimpleDateFormat) 存在以下主要问题:
- 非线程安全:
Date非线程安全,SimpleDateFormat在多线程环境下需同步。 - 设计混乱:
Date既包含日期又包含时间,年份从1900开始,月份从0开始,不符合直觉。 - 时区处理困难:时区逻辑分散在
Date,Calendar,SimpleDateFormat中,易出错。 - 可变性:
Date和Calendar是可变的,不符合函数式编程思想。
二、 Java 8 新日期时间 API 的核心设计原则
- 不可变性 (Immutability):所有核心类(如
LocalDate,LocalTime,LocalDateTime,ZonedDateTime)都是不可变的,线程安全。 - 清晰性 (Clarity):类名明确,如
LocalDate只表示日期,LocalTime只表示时间。 - 流畅的 API (Fluent API):方法链式调用,操作直观。
- ️时区明确:明确区分不含时区的本地日期时间和含时区的日期时间。
- 扩展性 (Extensibility):支持自定义日历系统。
三、 核心类详解与使用步骤
步骤1:理解基础类
新 API 位于 java.time 包下,核心类可分为以下几组:
-
本地日期时间 (无时区)
LocalDate:只包含日期,例如2023-10-27。LocalTime:只包含时间,例如14:30:15。LocalDateTime:包含日期和时间,例如2023-10-27T14:30:15。它是LocalDate和LocalTime的组合。
-
带时区的日期时间
ZonedDateTime:包含日期、时间和时区信息,例如2023-10-27T14:30:15+08:00[Asia/Shanghai]。它是LocalDateTime与时区 (ZoneId) 的结合。
-
时刻与持续时间
Instant:时间线上的一个瞬时点(时间戳),以 UTC 1970-01-01T00:00:00Z 为原点。用于机器时间。Duration:基于时间的持续时间,以秒和纳秒衡量。用于测量两个Instant或LocalTime之间的间隔。Period:基于日期的持续时间,以年、月、日衡量。用于测量两个LocalDate之间的间隔。
-
格式化和解析
DateTimeFormatter:线程安全的日期时间格式化器,替代SimpleDateFormat。
步骤2:创建对象
所有核心类都提供了静态工厂方法 now() 和 of()。
// 获取当前日期/时间
LocalDate today = LocalDate.now();
LocalTime currentTime = LocalTime.now();
LocalDateTime now = LocalDateTime.now();
ZonedDateTime zonedNow = ZonedDateTime.now(); // 使用系统默认时区
Instant instant = Instant.now();
// 指定日期/时间创建
LocalDate birthDate = LocalDate.of(1995, 5, 23); // 注意月份是正常的 1-12
LocalTime meetingTime = LocalTime.of(9, 30);
LocalDateTime projectDeadline = LocalDateTime.of(2023, 12, 31, 23, 59, 59);
ZonedDateTime meetingInTokyo = ZonedDateTime.of(projectDeadline, ZoneId.of("Asia/Tokyo"));
步骤3:解析与格式化
使用 DateTimeFormatter 进行字符串与日期时间对象的转换。
// 解析字符串
String dateStr = "20231027";
DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE;
LocalDate date = LocalDate.parse(dateStr, formatter);
String dateTimeStr = "2023-10-27 14:30";
DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(dateTimeStr, customFormatter);
// 格式化为字符串
String isoDate = today.format(DateTimeFormatter.ISO_LOCAL_DATE); // "2023-10-27"
String customFormat = now.format(customFormatter); // "2023-10-27 14:30"
步骤4:操作与计算
得益于不可变性,所有修改操作都会返回一个新对象。
LocalDate tomorrow = today.plusDays(1);
LocalDate nextWeek = today.plusWeeks(1);
LocalDate firstDayOfNextMonth = today.plusMonths(1).withDayOfMonth(1);
LocalDate lastDayOfMonth = today.with(TemporalAdjusters.lastDayOfMonth());
// 比较
boolean isBefore = tomorrow.isBefore(today);
boolean isAfter = tomorrow.isAfter(today);
boolean isLeapYear = today.isLeapYear();
// 获取字段
int year = today.getYear();
Month month = today.getMonth(); // 返回 Month 枚举
int dayOfMonth = today.getDayOfMonth();
DayOfWeek dayOfWeek = today.getDayOfWeek(); // 返回 DayOfWeek 枚举
步骤5:处理时区
使用 ZoneId 标识时区,ZonedDateTime 处理带时区的时间。
ZoneId shanghaiZone = ZoneId.of("Asia/Shanghai");
ZoneId newYorkZone = ZoneId.of("America/New_York");
LocalDateTime localMeeting = LocalDateTime.of(2023, 10, 28, 10, 0);
ZonedDateTime meetingInShanghai = ZonedDateTime.of(localMeeting, shanghaiZone);
ZonedDateTime meetingInNewYork = meetingInShanghai.withZoneSameInstant(newYorkZone);
System.out.println(meetingInShanghai); // 2023-10-28T10:00+08:00[Asia/Shanghai]
System.out.println(meetingInNewYork); // 2023-10-27T22:00-04:00[America/New_York]
步骤6:计算时间间隔
使用 Duration 和 Period。
LocalTime start = LocalTime.of(9, 0);
LocalTime end = LocalTime.of(17, 30);
Duration workDuration = Duration.between(start, end);
long hours = workDuration.toHours(); // 8
long minutes = workDuration.toMinutes(); // 510
LocalDate startDate = LocalDate.of(2023, 1, 1);
LocalDate endDate = LocalDate.of(2023, 10, 27);
Period period = Period.between(startDate, endDate);
int years = period.getYears(); // 0
int months = period.getMonths(); // 9
int days = period.getDays(); // 26
四、 与旧 API 的互操作
Java 8 提供了方便的方法在旧类与新 API 之间转换。
// java.util.Date 与 Instant 互转
Date oldDate = new Date();
Instant instantFromDate = oldDate.toInstant();
Date newDateFromInstant = Date.from(instant);
// java.util.Calendar 与 ZonedDateTime 互转 (思路)
Calendar calendar = Calendar.getInstance();
Instant instantFromCalendar = calendar.toInstant();
ZonedDateTime zdtFromCalendar = ZonedDateTime.ofInstant(instantFromCalendar, calendar.getTimeZone().toZoneId());
五、 总结与要点
- 线程安全:所有核心类不可变,本质线程安全。
- 清晰分工:
LocalDate,LocalTime,LocalDateTime用于本地上下文;ZonedDateTime和Instant用于需要时区或全球时间线的场景。 - 流畅操作:链式调用和明确的
plus,minus,with方法使代码易读。 - 强大工具:
TemporalAdjusters提供了复杂的日期调整(如下个周一),DateTimeFormatter提供了灵活且线程安全的格式化。 - 首选新API:在新项目中应完全使用
java.timeAPI。对于遗留代码,利用提供的转换方法进行桥接。
通过掌握这套新的日期时间API,你可以编写出更健壮、清晰且易于维护的处理日期和时间的Java代码。