java.util.Date 为什么被嫌弃?

如果你是一名 Java 开发者,java.util.Date 这个类你一定见过。

甚至可以说—— 你没主动用过,也一定被它“坑”过。

很多新同学都会有个疑问:

明明是 JDK 自带的类, 为什么老项目里一堆 Date, 但新项目却几乎看不到它了?

答案很简单一句话:

它太老了,老到不适合现代开发。

今天我们就用最直观的代码 + 最真实的坑,把这个“Java 的老古董”一次性讲清楚。

-****01-

**一个“活化石”级别的类 **

java.util.Date 诞生于 Java 1.0(1996 年)

那一年:

  • Java 还没泛型

  • 还没注解

  • 甚至连集合框架都不完整

你现在看到的 Date本质是 30 年前的设计产物

<pre data-start="539" data-end="600" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

Date date = newDate(); System.out.println(date);

</pre>

输出:

<pre data-start="607" data-end="643" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

MonSep2209:50:24CST2025

</pre>

看起来是不是很“正常”? 坑,正是从这里开始的。

[图片上传失败...(image-c3b22e-1767692579858)]

-****02-

**Date 的三大“反人类”设计缺陷 **

1. API 设计:

<pre data-start="731" data-end="888" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

Date date = newDate(); System.out.println("当前年月日:" + LocalDate.now()); System.out.println(date.getYear()); System.out.println(date.getMonth());

</pre>

输出:

<pre data-start="895" data-end="925" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

当前年月日:2025-09-22 125 8

</pre>

问题来了:

  • getYear() 返回 125

  • getMonth() 返回 8

原因是:

  • 年份:从 1900 年开始算****

  • 月份:从 0 开始(0 = 一月)****

2. 可变对象:

<pre data-start="1103" data-end="1274" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

Date date = newDate(2025 - 1900, 8, 22); System.out.println("原定日期: " + date); // 某个地方悄悄改了它 date.setYear(2026 - 1900); System.out.println("修改后的日期: " + date);

</pre>

输出:

<pre data-start="1281" data-end="1360" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

原定日期: Mon Sep 22 00:00:00 CST 2025 修改后的日期: Tue Sep 22 00:00:00 CST 2026

</pre>

在多线程环境下:

  • 谁改了?

  • 什么时候改的?

  • 为什么变了?

debug 到秃头都找不到[哭]

3. 时区语义混乱

<pre data-start="1464" data-end="1523" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

Date now = newDate(); System.out.println(now);

</pre>

你看到的是:

  • 系统默认时区的展示结果

Date 本身:

  • ****只存一个毫秒时间戳****

  • ****根本不关心时区****

展示和真实语义是分裂的,非常容易踩坑。

[图片上传失败...(image-78405f-1767692579858)]

-****03-

一个真实业务场景:算天数差

Date 算两个日期差几天,你只能这么干:

<pre data-start="1669" data-end="1908" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

Date date1 = newDate(125, 8, 22); // 2025-09-22 Date date2 = newDate(125, 9, 22); // 2025-10-22 long diff = date2.getTime() - date1.getTime(); long days = diff / (1000 * 60 * 60 * 24); System.out.println("相差天数: " + days);

</pre>

问题是:

  • 可读性差

  • 全是魔法数字

  • 时区 / 夏令时一变就翻车


[图片上传失败...(image-9fc0d7-1767692579858)]

-****04-

最后的最后

替代方案:Java 8 时间 API(java.time

Java 8 之后,官方已经给出了“正确答案”

1. API 清晰到像自然语言

<pre data-start="2045" data-end="2126" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

LocalDate date = LocalDate.of(2025, 9, 22); System.out.println(date);

</pre>

<pre data-start="2128" data-end="2180" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

LocalDateTime now = LocalDateTime.now();

</pre>

<pre data-start="2182" data-end="2269" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

ZonedDateTime shanghai = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));

</pre>

2. 不可变对象,天生线程安全

<pre data-start="2319" data-end="2427" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

LocalDate appointment = LocalDate.of(2025, 9, 22); LocalDate newDate = appointment.plusDays(30);

</pre>

  • 原对象不变

  • 返回新对象

  • 并发场景天然安全

3. 时间计算一行搞定

<pre data-start="2480" data-end="2593" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

long days = ChronoUnit.DAYS.between( LocalDate.of(2025, 9, 22), LocalDate.of(2025, 10, 22) );

</pre>

这才是 “业务代码”该有的样子

4. 时区是显式的,而不是“猜的”

<pre data-start="2649" data-end="2840" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

ZonedDateTime shanghai= ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); ZonedDateTime newYork= shanghai.withZoneSameInstant( ZoneId.of("America/New_York") );

</pre>

[图片上传失败...(image-697e8-1767692579857)]

老项目怎么办?

不用你“一刀切重构”

推荐做法:

  • 新代码:一律用 java.time

  • 旧代码交互:只在边界转换

<pre data-start="2947" data-end="3166" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

public static LocalDate toLocalDate(Date date) { if (date == null) { return null; } return date.toInstant() .atZone(ZoneId.systemDefault()) .toLocalDate(); }

</pre>

<pre data-start="3168" data-end="3399" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">

publicstatic LocalDateTime toLocalDateTime(Date date) { if (date == null) { return null; } return date.toInstant() .atZone(ZoneId.systemDefault()) .toLocalDateTime(); }

[图片上传失败...(image-64c285-1767692579856)]

</pre>

(面试直接背)

java.util.Date 是历史产物, 可变、反直觉、时区混乱、不适合现代并发系统

Java 8 之后,java.time 才是唯一正确选择

如果你在项目里还大量看到 Date

那不是你的错—— 但你有责任在新代码里终结它

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容