👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料: 

👉这是一个或许对你有用的开源项目

国产Star破10w的开源项目,前端包括管理后台、微信小程序,后端支持单体、微服务架构

RBAC权限、数据权限、SaaS多租户、商城、支付、工作流、大屏报表、ERP、CRMAI大模型、IoT物联网等功能:

  • 多模块:https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • 微服务:https://gitee.com/zhijiantianya/yudao-cloud

  • 视频教程:https://doc.iocoder.cn

【国内首批】支持 JDK17/21+SpringBoot3、JDK8/11+Spring Boot2双版本 

来源:juejin.cn/post/7552824412115812403


前言

如果你是一名 Java 开发者,很可能在日常工作中经常见到 java.util.Date 类。但你可能也注意到,现在 Java 开发中,我们通常会避免直接使用它。为什么会这样呢?今天我们就来聊聊这个话题。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

一、一个“历史悠久”的类

java.util.Date 自 Java 1.0 (1996年) 就存在了(可能比在座的各位出生的还早),可以说它是 Java 标准库中的"元老"了。但正如许多古老的事物一样,它带着当时设计的局限性:

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

控制台输出结果

Mon Sep 22 09:50:24 CST 2025

看起来很简单对吧?但问题就藏在这简单背后。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

二、设计缺陷一览

1. 令人困惑的 API 设计

Date 类的许多方法都已经过时(deprecated),而且设计上存在很多不合理之处:

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

控制台输出结果

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

看到问题了吗?年份从 1900 年开始计算,月份从 0 开始(0 表示一月,11 表示十二月)。这种反直觉的设计很容易导致错误。

2. 可变性带来的问题

Date 对象是可变的(mutable),这意味着你创建了一个 Date 对象后,它的值还可以被改变:

Date date = new Date(2025 - 1900, 8, 22);
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

这种可变性在多线程环境下尤其危险,容易导致难以调试的并发问题。

3. 时区处理困难

Date 实际上并不存储时区信息,它只是自 1970年1月1日00:00:00 GMT 以来的毫秒数。但它的 toString() 方法却使用系统默认时区来显示时间,这很容易造成混淆:

Date now = new Date();
System.out.println(now); // 输出取决于你的默认时区
4. 精度限制

Date 只能精确到毫秒级别,对于需要更高精度(如微秒、纳秒)的应用场景无法满足需求。

三、一个实际案例

假设我们要计算两个日期之间的天数差,使用 Date 会非常麻烦:

// 使用 Date 计算两个日期相差的天数(不推荐的方式)
Date date1 = new Date(125, 8, 22);   // 2025年9月22日
Date date2 = new Date(125, 9, 22);  // 2025年10月22日

long difference = date2.getTime() - date1.getTime();
long daysBetween = difference / (1000 * 60 * 60 * 24);
System.out.println("相差天数: " + daysBetween);

控制台输出结果

相差天数: 30

这段代码不仅难以阅读,还需要手动处理毫秒转换,容易出错。

四、更好的替代方案:Java 8 时间 API

自从 Java 8 (2014年) 引入了 java.time 包,我们有了现代、完善的时间日期处理 API。

1. 清晰的 API 设计
// 创建指定日期
LocalDate date = LocalDate.of(2025, 9, 22); // 2025年9月22日,直观明了!
System.out.println(date);
// 获取当前日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
// 获取当前日期时间,带时区
ZonedDateTime zonedNow = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(zonedNow);

控制台输出结果

2025-09-22
2025-09-22T10:30:21.743
2025-09-22T10:30:21.744+08:00[Asia/Shanghai]
2. 不可变性,线程安全
LocalDate appointment = LocalDate.of(2025, 9, 22);
// 下面的操作会返回新的对象,原对象不变
LocalDate newDate = appointment.plusDays(30);
3. 强大的计算功能
// 计算两个日期之间的天数
LocalDate date1 = LocalDate.of(2025, 9, 22);
LocalDate date2 = LocalDate.of(2025, 10, 22);

long daysBetween = ChronoUnit.DAYS.between(date1, date2);
System.out.println("相差天数: " + daysBetween);

控制台输出结果

相差天数: 30
4. 完善的时区支持
// 明确处理时区
ZonedDateTime shanghaiTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime newYorkTime = shanghaiTime.withZoneSameInstant(ZoneId.of("America/New_York"));

五、迁移建议

通常不建议,请慎重选择。

如果你在维护老项目,看到大量的 Date 代码,可以考虑以下迁移策略:

  • 新代码: 一律使用 java.time 包中的类

  • 与旧代码交互: 使用新增的转换方法:

/**
 * 将 java.util.Date 转换为 LocalDate
 * @param date java.util.Date
 * @return LocalDate(仅包含年月日),如果 date 为 null 返回 null
 */
public static LocalDate toLocalDate(Date date) {
    if (Objects.isNull(date)) {
        returnnull;
    }
    return date.toInstant()
               .atZone(ZoneId.systemDefault())
               .toLocalDate();
}

/**
 * 将 java.util.Date 转换为 LocalDateTime
 * @param date java.util.Date
 * @return LocalDateTime(包含年月日 + 时分秒),如果 date 为 null 返回 null
 */
public static LocalDateTime toLocalDateTime(Date date) {
    if (Objects.isNull(date)) {
        returnnull;
    }
    return date.toInstant()
               .atZone(ZoneId.systemDefault())
               .toLocalDateTime();
}

六、总结

java.util.Date 就像是编程世界中的"古董"——它有历史价值,但在现代开发中有更优的API可以替换。它的设计缺陷、线程安全问题以及难以使用的 API 都让我们有充分的理由转向更现代的 java.time API

希望这篇文章能帮助你理解为什么不推荐使用 java.util.Date,以及如何在你的项目中使用更好的替代方案。


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐