#新日期时间 API
面试官问:"Java 8 的日期时间 API 和旧的 Date 类有什么区别?"
候选人小叶答:"新的 API 更直观?"
面试官追问:"具体有什么区别?"
小叶说:"新的类是不可变的,线程安全?"
面试官追问:"那怎么把新 API 转成 Date 对象?"
小叶答不上来。
【面试官心理】 这道题考查的是候选人对 Java 8 Date-Time API 的理解。能说出不可变性、线程安全、ISO 8601 标准、并且知道新旧 API 转换的候选人,说明有生产经验。
#一、旧的 Date 类的问题 🔴
// 旧 API 的问题:
Date date = new Date(2024, 1, 1); // ❌ 月份从 0 开始!
Date date2 = new Date(2024 - 1900, 1, 1); // 需要减 1900
// 可变:不是线程安全的
Date date = new Date();
date.setTime(0); // 可以修改!
// 格式化不是线程安全的
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 多线程使用会出问题!#二、新 API 的核心类 🔴
#2.1 LocalDate(日期)
LocalDate date = LocalDate.now(); // 当前日期
LocalDate date2 = LocalDate.of(2024, 6, 15); // 指定日期
LocalDate date3 = LocalDate.parse("2024-06-15"); // 字符串解析
date.getYear(); // 2024
date.getMonth(); // JUNE
date.getMonthValue(); // 6
date.getDayOfMonth(); // 15
date.getDayOfWeek(); // SATURDAY
date.plusDays(1); // 加一天
date.minusYears(1); // 减一年
date.withDayOfMonth(1); // 设为月初#2.2 LocalTime(时间)
LocalTime time = LocalTime.now(); // 当前时间
LocalTime time2 = LocalTime.of(14, 30); // 14:30
LocalTime time3 = LocalTime.parse("14:30:00"); // 解析
time.getHour(); // 14
time.getMinute(); // 30
time.getSecond(); // 0#2.3 LocalDateTime(日期时间)
LocalDateTime dt = LocalDateTime.now();
LocalDateTime dt2 = LocalDateTime.of(2024, 6, 15, 14, 30);
// 组合
LocalDate date = LocalDate.of(2024, 6, 15);
LocalTime time = LocalTime.of(14, 30);
LocalDateTime dt3 = LocalDateTime.of(date, time);
// 互转
LocalDate d = dt.toLocalDate();
LocalTime t = dt.toLocalTime();#2.4 Instant(时间戳)
Instant now = Instant.now(); // UTC 时间戳
Instant epoch = Instant.EPOCH; // 1970-01-01 00:00:00 UTC
Instant later = now.plusSeconds(3600); // 加一小时
long epochSeconds = now.getEpochSecond(); // 秒
long nano = now.toEpochMilli(); // 毫秒#2.5 ZonedDateTime(带时区)
ZonedDateTime zdt = ZonedDateTime.now(); // 当前时区
ZonedDateTime zdt2 = ZonedDateTime.now(ZoneId.of("America/New_York"));
ZonedDateTime zdt3 = ZonedDateTime.parse("2024-06-15T14:30:00+08:00[Asia/Shanghai]");
// 转换时区
ZonedDateTime utc = zdt.withZoneSameInstant(ZoneId.of("UTC"));#三、核心特性 🔴
#3.1 不可变性
// ✅ 新 API 是不可变的
LocalDate date = LocalDate.of(2024, 6, 15);
LocalDate tomorrow = date.plusDays(1);
// date 还是 2024-06-15,tomorrow 是 2024-06-16
// ✅ 线程安全
LocalDate date = LocalDate.now(); // 共享无需同步#3.2 格式化与解析
// 使用 DateTimeFormatter
LocalDate date = LocalDate.of(2024, 6, 15);
// 预定义格式
String s1 = date.format(DateTimeFormatter.ISO_LOCAL_DATE); // "2024-06-15"
// 自定义格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String s2 = date.format(formatter); // "2024/06/15"
// 解析
LocalDate parsed = LocalDate.parse("2024/06/15", formatter);
// ✅ DateTimeFormatter 是线程安全的!
// SimpleDateFormat 不是线程安全的!#四、与旧 API 的转换 🟡
// Date → LocalDateTime
Date date = new Date();
Instant instant = date.toInstant();
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
// LocalDateTime → Date
LocalDateTime ldt2 = LocalDateTime.now();
Instant instant2 = ldt2.atZone(ZoneId.systemDefault()).toInstant();
Date date2 = Date.from(instant2);
// Timestamp → LocalDateTime
Timestamp ts = new Timestamp(System.currentTimeMillis());
LocalDateTime ldt3 = ts.toLocalDateTime();
// LocalDateTime → Timestamp
LocalDateTime ldt4 = LocalDateTime.now();
Timestamp ts2 = Timestamp.valueOf(ldt4);#五、Duration 与 Period 🟡
// Duration:时间间隔(秒、纳秒)
Duration d1 = Duration.ofHours(2);
Duration d2 = Duration.between(start, end);
// Period:日期间隔(年、月、日)
Period p = Period.ofYears(1).plusMonths(2).plusDays(15);
// 计算差异
LocalDate start = LocalDate.of(2024, 1, 1);
LocalDate end = LocalDate.of(2024, 6, 15);
long days = ChronoUnit.DAYS.between(start, end); // 166 天
Period period = Period.between(start, end); // P5M14D#六、生产避坑 🟡
#6.1 数据库日期映射
// MyBatis
@Select("SELECT * FROM orders WHERE created_at = #{date}")
List<Order> findByDate(@Param("date") LocalDate date);
// JPA
@Entity
public class Order {
@Column(name = "created_at")
private LocalDateTime createdAt; // 直接映射
}#6.2 JSON 序列化
// Jackson 需要配置模块
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
// 或 Spring Boot 2.0+ 自动支持#七、追问升级
面试官:"Java 8 Date-Time API 为什么要设计成不可变?"
// 原因:
// 1. 线程安全:日期时间对象经常被共享
// 2. 可预测性:修改不会影响其他引用
// 3. 更好的 API 设计:plus/minus/with 方法返回新实例
// 4. 函数式友好:可用于 Stream、Lambda