为什么要学习新的日期时间API?
在Java 8之前,开发者主要使用Date和Calendar类处理日期时间。但这些旧API存在诸多问题,Java 8引入的全新日期时间API彻底解决了这些问题。
旧API的四大罪状:
- 可变类:Date和Calendar都是可变的,线程不安全
- 月份混乱:Calendar月份从0开始计数(0=一月)
- 命名混乱:Date并不表示日期,只是时间戳
- 时区麻烦:处理时区非常复杂
// 旧API的痛苦
Date date = new Date(2026 - 1900, 5, 15); // 月份为什么要减1900,为什么从0开始?
Calendar cal = Calendar.getInstance();
cal.set(2026, 5, 15); // 5代表六月!不是五月!
date.setMonth(11); // 什么?我可以直接改内部状态?
// 多线程环境下,这些类是危险的!
Java 8+ 新API的六大核心类
| 类 | 完整类名 | 说明 | 示例 |
|---|---|---|---|
| LocalDate | java.time.LocalDate | 日期(年-月-日) | 2026-06-15 |
| LocalTime | java.time.LocalTime | 时间(时:分:秒) | 14:30:00 |
| LocalDateTime | java.time.LocalDateTime | 日期时间 | 2026-06-15T14:30:00 |
| ZonedDateTime | java.time.ZonedDateTime | 带时区的日期时间 | 2026-06-15T14:30+08:00 |
| Instant | java.time.Instant | 时间戳(UTC) | 2026-06-15T06:30:00Z |
| Duration | java.time.Duration | 时间间隔(秒/纳秒) | PT2H30M |
| Period | java.time.Period | 日期间隔(年/月/日) | P1M2D |
理解这三者的区别
想象你在规划一次旅行:
LocalDate就像日历上的日期——"6月15日",告诉你哪一天,但不关心几点。
LocalTime就像手表上的时间——"下午2:30",告诉你什么时候,但不关心是哪天。
LocalDateTime就像完整的日程安排——"6月15日下午2:30",日期和时间都有。
ZonedDateTime就像带时区的会议通知——"北京时间6月15日下午2:30",明确指出在哪个时区。
新API的六大优势
线程安全
所有日期时间类都是不可变类,天然线程安全
API清晰
不再有月份从0开始等反人类设计
时区支持
完整的时区处理,ZonedDateTime让你轻松处理全球时间
精度灵活
从秒到纳秒,不同场景用不同精度
方法丰富
plusDays、minusHours、withSecond等链式调用
遵循ISO标准
遵循ISO-8601标准,字符串解析和格式化标准化
新旧API对比一览
| 场景 | 旧API | 新API |
|---|---|---|
| 获取当前日期 | new Date() | LocalDate.now() |
| 创建指定日期 | new Date(2026-1900, 5, 15) | LocalDate.of(2026, 6, 15) |
| 日期加法 | cal.add(Calendar.DAY, 7) | date.plusDays(7) |
| 获取年份 | cal.get(Calendar.YEAR) | date.getYear() |
| 月份(Human) | cal.get(Calendar.MONTH) + 1 | date.getMonthValue() |
| 格式化 | new SimpleDateFormat("yyyy-MM-dd") | DateTimeFormatter.ofPattern("yyyy-MM-dd") |
| 线程安全 | 否 | 是 |
快速入门示例
import java.time.*;
public class DateTimeQuickStart {
public static void main(String[] args) {
// LocalDate - 只关心日期
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(2000, 1, 1); // 不再是(2000-1900, 0, 1)!
System.out.println("今天: " + today);
System.out.println("生日: " + birthday);
// LocalTime - 只关心时间
LocalTime now = LocalTime.now();
LocalTime meeting = LocalTime.of(14, 30); // 下午2:30
System.out.println("现在: " + now);
System.out.println("会议时间: " + meeting);
// LocalDateTime - 日期和时间都有
LocalDateTime nowDateTime = LocalDateTime.now();
LocalDateTime appointment = LocalDateTime.of(2026, 6, 15, 10, 30);
System.out.println("当前时间: " + nowDateTime);
System.out.println("预约: " + appointment);
// Instant - 时间戳(机器友好)
Instant instant = Instant.now();
System.out.println("时间戳: " + instant);
System.out.println("秒数: " + instant.getEpochSecond());
}
}
小结
- 旧API问题多:可变、月份从0开始、线程不安全、时区处理复杂
- LocalDate处理日期(年月日)
- LocalTime处理时间(时分秒)
- LocalDateTime处理日期时间(年月日的时分秒)
- ZonedDateTime处理带时区的日期时间
- Instant表示UTC时间戳
- 新API线程安全、API清晰、遵循ISO标准