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

23 阅读3分钟

沉默是金,总会发光

大家好,我是沉默

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

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

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

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

答案很简单一句话:

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

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

**-**01-

一个“活化石”级别的类

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

那一年:

  • Java 还没泛型
  • 还没注解
  • 甚至连集合框架都不完整

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

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

输出:

MonSep2209:50:24CST2025

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

图片

- 02-

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

1. API 设计:

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

输出:

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

**
**

问题来了:

  • getYear() 返回 125
  • getMonth() 返回 8

原因是:

  • 年份:从 1900 年开始算
  • 月份:从 0 开始(0 = 一月)

2. 可变对象:

Date date = newDate(2025 - 1900822);
System.out.println("原定日期: " + date);

// 某个地方悄悄改了它
date.setYear(2026 - 1900);

System.out.println("修改后的日期: " + date);

输出:

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

**
**

在多线程环境下:

  • 谁改了?
  • 什么时候改的?
  • 为什么变了?

debug 到秃头都找不到[哭]

**
**

3. 时区语义混乱

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

你看到的是:

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

但 Date 本身:

  • 只存一个毫秒时间戳
  • 根本不关心时区

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

图片

- 03-

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

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

Date date1 = newDate(125822);  // 2025-09-22
Date date2 = newDate(125922);  // 2025-10-22

long diff = date2.getTime() - date1.getTime();
long days = diff / (1000 * 60 * 60 * 24);

System.out.println("相差天数: " + days);

问题是:

  • 可读性差
  • 全是魔法数字
  • 时区 / 夏令时一变就翻车

图片

**-**04-

最后的最后

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

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

1. API 清晰到像自然语言

LocalDate date = LocalDate.of(2025922);
System.out.println(date);
LocalDateTime now = LocalDateTime.now();
ZonedDateTime shanghai =
ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));

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

LocalDate appointment = LocalDate.of(2025922);
LocalDate newDate = appointment.plusDays(30);
  • 原对象不变
  • 返回新对象
  • 并发场景天然安全

3. 时间计算一行搞定

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

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

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

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

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

图片

老项目怎么办?

不用你“一刀切重构”

推荐做法:

  • 新代码:一律用 java.time
  • 旧代码交互:只在边界转换
public static LocalDate toLocalDate(Date date) {
if (date == null) {
return null;
    }
return date.toInstant()
               .atZone(ZoneId.systemDefault())
               .toLocalDate();
}
publicstatic LocalDateTime toLocalDateTime(Date date) {
if (date == null) {
return null;
    }
return date.toInstant()
               .atZone(ZoneId.systemDefault())
               .toLocalDateTime();
}

(面试直接背)

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

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

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

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

图片

**-**05-

粉丝福利

点点关注,送你互联网大厂面试题库,如果你正在找工作,又或者刚准备换工作。可以仔细阅读一下,或许对你有所帮助!

image.png

image.pngimage.png