点击上方“程序员蜗牛g”,选择“设为星标”
跟蜗牛哥一起,每天进步一点点
程序员蜗牛g
大厂程序员一枚 跟蜗牛一起 每天进步一点点
33篇原创内容
**
公众号
Java新日期/时间API于Java 8中引入,并在Java 21中得到了增强,为现代应用程序中的日期和时间处理提供了强大的解决方案。
传统日期/时间API(java.util.Date、Calendar、SimpleDateFormat)存在以下问题:
- 可变(非线程安全)
- 令人困惑(月份从0开始,但日期从1开始)
- 容易出错(日期和时间混在一个类中)
- 时间区处理能力弱
- 可读性差
2.实战案例
2.1 旧日期API
- 可变性问题
public static void main(String[] args) { // 当前时间 Date date = new Date(); System.out.println("原始值: " + date); // 修改同一对象 date.setTime(date.getTime() + 1000000000L); System.out.println("修改后: " + date) ;}
输出结果
原始值: Fri Oct 7 08:43:44 CST 2025修改后: Tue Oct 18 22:30:24 CST 2025
问题:日期对象被修改——这在多线程下是非常不友好的。
- 令人困惑的API
public static void main(String[] args) { Calendar cal = Calendar.getInstance(); // 1月,11日 cal.set(2025, 0, 11); System.out.println(cal.getTime());}
问题:月份从0开始。许多开发者会无意中引发了错误。
- 线程不安全的格式化
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");public static void main(String[] args) throws Exception { final int max = 100; final int iterations = 1000; CyclicBarrier barrier = new CyclicBarrier(max) ; Runnable task = () -> { try { barrier.await(); // 所有线程在此同步 for (int i = 0; i < iterations; i++) { // 这里调用 format 是不安全的! FORMAT.format(new Date()); } } catch (Exception e) { e.printStackTrace(); System.err.println("线程 " + Thread.currentThread().getName() + " 抛出异常: " + e.getClass().getSimpleName()); } }; for (int i = 0; i < max; i++) { new Thread(task, "T-" + i).start(); }}
问题:SimpleDateFormat 并非线程安全的,导致在并发应用中输出结果损坏。
2.2 新日期API(Java8+)
新API位于java.time包中:
- 不可变性:不会意外修改
- 职责明确分离:LocalDate → 仅日期;LocalTime → 仅时间;LocalDateTime → 日期时间(无时区);ZonedDateTime → 带时区
- 流畅且易读的API
- 不可变且安全
public static void main(String[] args) { LocalDate today = LocalDate.now(); System.out.println("当前日期: " + today); // 返回新的对象 LocalDate nextWeek = today.plusWeeks(1); System.out.println("下一周日期: " + nextWeek); // 并没有发生变化 System.out.println("日期保持不变: " + today);}
安全:原始内容今日从未被修改。
- 告别基于 0 的月份混淆
public static void main(String[] args) { // 月份从1开始 LocalDate date = LocalDate.of(2025, 1, 11) ; System.out.println(date);}
输出结果
2025-01-11
明确:月份为1至12,符合预期。
- 线程安全的格式化
public static void main(String[] args) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); LocalDateTime now = LocalDateTime.now(); String formatted = now.format(formatter); System.out.println(formatted);}
安全:DateTimeFormatter 是不可变的且线程安全的类。
总结
旧版API(Date、Calendar、SimpleDateFormat)
- 在多线程应用中存在可变性缺陷
- 令人困惑(月份从0开始计数,职责划分不明确)
- 非线程安全
- 难以使用
新版API(java.time)
- 不可变且线程安全
- 类型清晰(LocalDate、LocalTime等)
- 易于格式化和解析
- 提供完善的时区支持
- 可读性更强
2.3 新旧API对比
- 获取当前日期
传统方法
Date today = new Date();System.out.println(today);
现代方法
LocalDate today = LocalDate.now();System.out.println(today);
- 为日期添加天数
传统方法
Calendar c = Calendar.getInstance();c.add(Calendar.DAY_OF_MONTH, 5);System.out.println(c.getTime());
现代方法
LocalDate today = LocalDate.now();LocalDate future = today.plusDays(5);System.out.println(future);
不可变的原始日期保持不变。
- 解析和格式化
传统方法
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");Date date = sdf.parse("11/01/2025");System.out.println("解析: " + date);String formatted = sdf.format(new Date());System.out.println("格式化: " + formatted);
现代方法
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");LocalDate date = LocalDate.parse("11/01/2025", formatter);System.out.println("解析:"+ date);String formatted = LocalDate.now().format(formatter); System.out.println("格式化: "formatted);
线程安全、不可变且更简洁。
- 时区处理
传统方法
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("Asia/Shanghai"));System.out.println(c.getTime());
现代方法
ZonedDateTime nyTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));System.out.println(nyTime);
明确、人性化且避免错误。
- 获取当前时间戳
传统方法
long millis = System.currentTimeMillis();System.out.println(millis);
现代方法
Instant now = Instant.now();System.out.println(now);
提供即时时间戳,适用于持久化存储和API调用。
2.4 最佳实践
- 始终使用现代日期/时间 API
理论:传统的 java.util.Date 和 Calendar 类存在根本性的设计缺陷,包括可变性问题、API 设计欠佳、月份索引混乱(1 月 = 0)、时区支持有限以及性能问题。
// 传统API// java.util.Date oldDate = new java.util.Date();// java.util.Calendar calendar java.util.Calendar.getInstance();// 现代APILocalDate today = LocalDate.now();LocalDateTime now = LocalDateTime.now();Instant timestamp = Instant.now();
- 根据使用场景选择正确的类
理论:Java日期/时间API针对不同的时间概念提供了专门的类。使用错误的类可能会导致难以察觉的错误和性能问题。
- LocalDate — 不包含时间的日期(生日、截止日期、节假日)
- LocalTime — 不包含日期的时间(营业时间、闹钟时间)
- LocalDateTime — 不包含时区的日期和时间(适用于同一时区的应用)
- ZonedDateTime — 包含时区的日期和时间(适用于全球应用、日程安排)
- Instant — UTC时间点(时间戳、日志记录、系统事件)
- Duration — 基于时间的量(小时、分钟、秒)
- Period — 基于日期的量(年、月、日)
public static void demonstrateClasses() { // 当前日期和时间 LocalDate today = LocalDate.now(); LocalTime currentTime = LocalTime.now(); LocalDateTime now = LocalDateTime.now(); Instant timestamp = Instant.now(); System.out.println("当前日期: " + today); System.out.println("当前时间: " + currentTime); // 14:30:45.123 System.out.println("现在时间: " + now); // 2025-05-31T14:30:45.123 System.out.println("UTC timestamp: " + timestamp); // 2025-05-31T09:00:45.1232 LocalDate christmas = LocalDate.of(2025, Month.DECEMBER, 25); LocalTime meetingTime = LocalTime.of(14, 30); LocalDateTime appointment = LocalDateTime.of(christmas, meetingTime); LocalDate nextWeek = today.plusWeeks(1); LocalDate lastMonth = today.minusMonths(1);}
- 优雅处理解析错误
理论:日期/时间解析因格式变化、区域设置差异、无效日期和模糊格式而固有易出错。请始终通过多次格式尝试实现稳健的错误处理。
// 处理多种格式的灵活解析器public static LocalDate parseFlexibleDate(String dateString) { DateTimeFormatter[] formatters = { DateTimeFormatter.ISO_LOCAL_DATE, // e.g., 2025-10-08 DateTimeFormatter.ofPattern("dd-MM-yyyy"), // e.g., 08-10-2025 DateTimeFormatter.ofPattern("MM/dd/yyyy"), // e.g., 10/08/2025 DateTimeFormatter.ofPattern("dd/MM/yyyy") // e.g., 08/10/2025 }; for (DateTimeFormatter formatter : formatters) { try { // 尝试使用当前格式化程序进行解析 return LocalDate.parse(dateString, formatter); } catch (DateTimeParseException e) { System.out.println("Failed to parse with format: " + formatter + " → " + e.getMessage()); } } // 如果没有格式化程序工作,则抛出一个明确的异常 throw new IllegalArgumentException("Invalid date format: " + dateString);}public static void main(String[] args) { String[] testDates = { "2025-10-08", "08-10-2025", "10/08/2025", "08/10/2025", "invalid-date" }; for (String dateStr : testDates) { try { LocalDate parsed = parseFlexibleDate(dateStr); System.out.println("Parsed Date: " + parsed); } catch (IllegalArgumentException e) { System.out.println(" Error: " + e.getMessage()); } }}
如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。
关注公众号:woniuxgg,在公众号中回复:笔记 就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!