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交互时,才考虑使用旧的日期类。

相关推荐
叫致寒吧4 小时前
Tomcat详解
java·tomcat
同学小张5 小时前
【端侧AI 与 C++】1. llama.cpp源码编译与本地运行
开发语言·c++·aigc·llama·agi·ai-native
踢球的打工仔6 小时前
PHP面向对象(7)
android·开发语言·php
S***26758 小时前
基于SpringBoot和Leaflet的行政区划地图掩膜效果实战
java·spring boot·后端
汤姆yu8 小时前
基于python的外卖配送及数据分析系统
开发语言·python·外卖分析
Yue丶越8 小时前
【C语言】字符函数和字符串函数
c语言·开发语言·算法
马剑威(威哥爱编程)8 小时前
鸿蒙6开发视频播放器的屏幕方向适配问题
java·音视频·harmonyos
JIngJaneIL9 小时前
社区互助|社区交易|基于springboot+vue的社区互助交易系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·社区互助
翔云 OCR API9 小时前
人脸识别API开发者对接代码示例
开发语言·人工智能·python·计算机视觉·ocr
V***u4539 小时前
MS SQL Server partition by 函数实战二 编排考场人员
java·服务器·开发语言