java的java.sql.Date和java.util.Date的区别,应该怎么使用

我们来详细解析 java.sql.Datejava.util.Date的区别、联系以及正确使用方法。

核心结论

  • java.util.Date : 表示一个特定的时间瞬间,精确到毫秒 。它包含日期时间两部分信息。是Java中表示日期时间的原始类。

  • java.sql.Date : 是 java.util.Date的子类,但它只表示日期 部分(年、月、日),时间部分(时、分、秒、毫秒)被强制设置为00:00:00。它专为与SQL数据库中的 DATE类型匹配而设计。


详细对比

特性 java.util.Date java.sql.Date
包路径 java.util java.sql
继承关系 父类 子类(extends java.util.Date
表示内容 一个具体的时间点(日期 + 时间 + 毫秒) 仅日期(年-月-日)
时间部分 保留时、分、秒、毫秒 被强制设为 00:00:00(对应时区)
设计目的 通用的日期时间表示 专门用于与SQL数据库的 DATE类型交互
数据库对应类型 无直接对应类型 SQL DATE(e.g., MySQL DATE, Oracle DATE)
典型用途 在Java应用程序内部表示一个完整的时间点 在JDBC操作中,作为PreparedStatement的参数或ResultSet的返回值

如何正确使用

1. 在JDBC操作中的使用

这是 java.sql.Date最主要的使用场景。JDBC驱动程序知道如何将 java.sql.Date转换为数据库中的 DATE类型,反之亦然。

示例:将生日信息存入数据库

假设数据库表 users中有一个 birthday字段,类型为 DATE

复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Date; // 注意:这里导入的是 java.sql.Date

public class JdbcExample {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/your_database";
        String user = "username";
        String password = "password";

        // 假设我们从用户输入或业务逻辑中得到了一个 java.util.Date 对象
        java.util.Date utilDate = new java.util.Date(); // 这里代表当前日期和时间

        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            String sql = "INSERT INTO users (name, birthday) VALUES (?, ?)";
            PreparedStatement pstmt = conn.prepareStatement(sql);

            pstmt.setString(1, "张三");

            // 关键步骤:将 java.util.Date 转换为 java.sql.Date
            // 使用 java.sql.Date 的构造函数,传入毫秒数
            java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
            pstmt.setDate(2, sqlDate); // 正确:使用 setDate 方法

            pstmt.executeUpdate();
            System.out.println("数据插入成功!");

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

从数据库读取:

复制代码
// ... 连接数据库的代码 ...
String sql = "SELECT birthday FROM users WHERE id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 1);
ResultSet rs = pstmt.executeQuery();

if (rs.next()) {
    // 从ResultSet中直接获取 java.sql.Date
    java.sql.Date sqlBirthday = rs.getDate("birthday");
    
    // 如果需要转换为 java.util.Date,因为继承关系,可以直接赋值
    java.util.Date utilBirthday = sqlBirthday; // 多态,向上转型
    System.out.println(utilBirthday); // 输出类似:Thu Nov 07 00:00:00 CST 2024
    // 注意:时间部分会是 00:00:00
}
2. 类型转换

java.util.Date-> java.sql.Date

这是最常见的转换需求。不能直接强制转换,必须通过毫秒值来构造。

复制代码
java.util.Date utilDate = new java.util.Date();

// 正确方式:使用毫秒数构造函数
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());

// 错误方式:强制转换 (编译可能通过,但运行时行为不确定,强烈不建议!)
// java.sql.Date badSqlDate = (java.sql.Date) utilDate;

java.sql.Date-> java.util.Date

由于继承关系,这是安全的,可以直接赋值。

复制代码
java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());
java.util.Date utilDate = sqlDate; // 自动向上转型,总是安全

重要注意事项和常见陷阱

  1. 时间信息丢失 : 当你将一个包含非零时间的 java.util.Date转换为 java.sql.Date时,时间部分(时、分、秒、毫秒)会永久丢失。转换后,时间永远是 00:00:00

  2. 不要混淆 setDatesetTimestamp

    • 如果你的数据库字段是 DATE类型,使用 PreparedStatement.setDate(),参数是 java.sql.Date

    • 如果你的数据库字段是 DATETIMETIMESTAMP类型,你应该使用 PreparedStatement.setTimestamp(),参数是 java.sql.Timestamp(它是另一个子类,保留了纳秒精度)。

  3. 时区问题 : 虽然 java.sql.Date内部存储的是自1970年1月1日00:00:00 GMT以来的毫秒数,但当调用 toString()方法时,它会使用JVM的默认时区来格式化为 YYYY-MM-DD的形式。这有时会导致令人困惑的结果,比如在GMT+8时区,1970-01-01 08:00:00 GMT会被显示为 1970-01-01


现代最佳实践:使用 Java 8 的 Date/Time API

java.util.Datejava.sql.Date都是旧的API,存在设计缺陷(如可变性、线程不安全、API难用等)。在现代Java开发中(Java 8及以上),强烈推荐使用 java.time包下的新日期时间API。

旧类 新API (java.time) 说明
java.util.Date Instant, LocalDateTime, ZonedDateTime 表示时间点或带时区的日期时间
java.sql.Date LocalDate 仅表示日期,完美对应SQL DATE

在JDBC中使用新API(需要JDBC 4.2及以上驱动支持):

复制代码
// 写入数据库
LocalDate localDate = LocalDate.of(1990, 1, 1);
PreparedStatement pstmt = conn.prepareStatement("INSERT INTO users (birthday) VALUES (?)");
pstmt.setObject(1, localDate); // 直接使用 setObject
pstmt.executeUpdate();

// 从数据库读取
ResultSet rs = pstmt.executeQuery("SELECT birthday FROM users");
if (rs.next()) {
    // 直接使用 getObject 转换为 LocalDate
    LocalDate birthday = rs.getObject("birthday", LocalDate.class);
    System.out.println("生日: " + birthday); // 输出: 1990-01-01
}

如果你的JDBC驱动版本较老,不支持直接转换,可以借助 java.sql.Date作为桥梁:

复制代码
// LocalDate -> java.sql.Date
LocalDate localDate = LocalDate.now();
java.sql.Date sqlDate = java.sql.Date.valueOf(localDate);

// java.sql.Date -> LocalDate
java.sql.Date sqlDateFromDB = rs.getDate("birthday");
LocalDate localDate = sqlDateFromDB.toLocalDate();

总结

  1. 根本区别java.util.Date是日期+时间,java.sql.Date是仅日期(是前者的子类)。

  2. 核心用途java.sql.Date专为与数据库 DATE类型交互而设计。

  3. 正确使用 :在JDBC中,用 PreparedStatement.setDateResultSet.getDate来操作 java.sql.Date

  4. 转换方法 :通过 getTime()方法获得的毫秒数在两者间安全转换。

  5. 现代实践优先使用 java.timeAPI(如 LocalDate,它更安全、更清晰、功能更强大。只有在必须与旧API交互时,才考虑使用旧的日期类。

相关推荐
循环过三天2 小时前
3.4、Python-集合
开发语言·笔记·python·学习·算法
高山上有一只小老虎3 小时前
java 正则表达式大全
java·正则表达式
_院长大人_3 小时前
设计模式-工厂模式
java·开发语言·设计模式
MATLAB代码顾问3 小时前
MATLAB实现决策树数值预测
开发语言·决策树·matlab
凌波粒4 小时前
MyBatis完整教程IDEA版(2)--ResultMap/注解/一对多/多对一/lombok/log4j
java·intellij-idea·mybatis
蓝-萧4 小时前
【玩转全栈】----Django基本配置和介绍
java·后端
priority_key4 小时前
排序算法:堆排序、快速排序、归并排序
java·后端·算法·排序算法·归并排序·堆排序·快速排序
不染尘.5 小时前
2025_11_7_刷题
开发语言·c++·vscode·算法
似水এ᭄往昔5 小时前
【C++】--stack和queue
开发语言·c++