专业编程教程与实战项目分享平台

网站首页 > 技术文章 正文

Java 中 java.util.Date 与 java.sql.Date 有什么区别?

ins518 2025-09-02 23:43:23 技术文章 6 ℃ 0 评论

Java 里的 java.util.Datejava.sql.Date 绝对是那种看起来不起眼但能搞得你 Debug 到半夜的“坑王”。

我们先从表面上看,java.sql.Date 是继承自 java.util.Date 的,按理说是“子承父业”,兼容没问题对吧?但实际上,这俩压根儿不是一个世界的。java.util.Date 是 Java 标准库里提供的“通用型”时间类,而 java.sql.Date 则是 JDBC 里为了数据库操作特化出来的版本,核心区别就在于:一个有时间,一个没时间

就是这么沙雕的区别,java.sql.Date 把时间部分(小时、分钟、秒)全都砍了个干净,只保留年月日。为什么这么干?因为很多数据库的 DATE 类型本来就只存年月日,比如 MySQL 的 DATE 字段。所以 java.sql.Date 就跟着这个“传统艺能”走了,给你个精确到天的“日子”。

来看段代码你就明白了:

public class DateExample {
    public static void main(String[] args) {
        java.util.Date utilDate = new java.util.Date();
        java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());

        System.out.println("utilDate: " + utilDate); // 有时间
        System.out.println("sqlDate: " + sqlDate);   // 只有年月日
    }
}

输出可能是这样的:

utilDate: Thu Jun 05 15:30:12 CST 2025
sqlDate: 2025-06-05

你看,时间部分直接没了!这就导致很多时候你把 java.sql.Date 传回前端,前端一看没时间,直接懵了...

我遇到过最离谱的是,有个哥们把 java.sql.Date 强转成 java.util.Date,然后还奇怪为啥结果有问题。讲真,这是个巨坑。虽然它们在继承关系上确实可以强转,但你转完之后,它的“灵魂”还是那个没有时间的 sql.Date

所以你如果真的要拿 java.sql.Date 去做一些带时间的运算,比如和当前时间比较,或者转换成时间戳,那基本就是拿刀割自己手指头。比如这个例子:

java.sql.Date sqlDate = java.sql.Date.valueOf("2025-06-05");
long millis = sqlDate.getTime(); // 你以为这是某个时间点?
Date converted = new Date(millis);
System.out.println(converted); // 时间是 00:00:00 啊兄弟!

你以为你得到了“某一天的某个时刻”,结果其实是“那天的零点”... 这还不如直接用 LocalDate 呢。

对,说到这个就得扯一下 Java 8 的时间新 API——java.time.*。这波更新真的可以说是“Date 的反攻”。像 LocalDateLocalDateTime 就分别对应 java.sql.Datejava.util.Date 的干净替代品,前者精确到天,后者精确到秒,而且线程安全,没有时区困扰,API 超清晰,看着就让人舒服。

LocalDate localDate = LocalDate.now();
LocalDateTime localDateTime = LocalDateTime.now();

System.out.println("LocalDate: " + localDate);       // 2025-06-05
System.out.println("LocalDateTime: " + localDateTime); // 2025-06-05T15:45:30

最重要的是,java.sql.Datejava.sql.Timejava.sql.Timestamp 都有对应的 toLocalDate()toLocalDateTime() 方法,基本可以无痛迁移。

但你要说现实项目里能不能彻底抛弃 java.sql.Date?我只能说:想多了。只要你在用 JDBC,尤其是用 MyBatis 或 Hibernate 这种 ORM 框架,它们底层还是会默认你搞点 java.sql.Date 出来。所以我个人的策略是:入库出库都用 LocalDate / LocalDateTime,中间统一转换

比如用 MyBatis 的 TypeHandler 机制,写一个 LocalDatejava.sql.Date 的 Handler:

@MappedTypes(LocalDate.class)
public class LocalDateTypeHandler extends BaseTypeHandler<LocalDate> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
                LocalDate parameter, JdbcType jdbcType) throws SQLException {
        ps.setDate(i, Date.valueOf(parameter));
    }

    @Override
    public LocalDate getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Date date = rs.getDate(columnName);
        return date != null ? date.toLocalDate() : null;
    }

    // 其他两个 get 方法略
}

配上 XML 或注解,就能实现 LocalDate 的透明持久化,再也不用在每个 Entity 里扔一堆类型转换的代码了

另外顺带一提,还有不少人会问:“那 java.sql.Timestamp 是啥角色?”很简单,它就是 java.util.Date + 纳秒精度的变种,主要是数据库里有精确到秒甚至微秒的 TIMESTAMP 字段时用的。但我一般都尽量避免用它,因为那玩意儿坑更多,特别是跨数据库还经常有精度兼容性问题(MySQL 精确到毫秒,Oracle 精确到纳秒,一导出 CSV 就炸了...)

回到最初的问题:到底 java.util.Datejava.sql.Date 有啥区别?

如果用一句话总结就是:

java.util.Date 是全能选手,啥时间信息都有; java.sql.Date 是简化版,为了和数据库的 DATE 类型打配合,砍掉了时间部分,只保留“日期”。

但这区别,常常在你不注意的时候就搞事,所以我现在干脆上来就用 LocalDateLocalDateTime,实在不行中间过渡一下,也总比在项目上线前踩坑强

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表