核心概念
java.util.Date:Java 最早、最基础的日期时间类,它代表了一个特定的瞬间,精确到毫秒。注意:它不包含时区信息,并且很多方法(如getYear(),getMonth())已经过时(@Deprecated),不推荐在新代码中使用。java.text.SimpleDateFormat:一个不线程安全的类,用于日期和时间的格式化和解析,这是将String和Date互转最传统的方式。java.time包 (Java 8+):Java 8 引入的一全新的、不可变且线程安全的日期时间 API,这是目前强烈推荐使用的现代方式,它包含LocalDate,LocalDateTime,ZonedDateTime等类,以及DateTimeFormatter用于格式化。
使用旧 API (java.util.Date 和 SimpleDateFormat)
这种方式在旧代码中非常普遍,但由于 SimpleDateFormat 的线程安全问题,使用时需要特别小心。

(图片来源网络,侵删)
基本转换
这是最直接的转换方式。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class StringToDateOld {
public static void main(String[] args) {
// 1. 定义日期字符串和格式
String dateString = "2025-10-27 10:30:00";
String pattern = "yyyy-MM-dd HH:mm:ss";
// 2. 创建 SimpleDateFormat 对象
// !!! 重要:SimpleDateFormat 不是线程安全的,不要在多线程环境下共享同一个实例 !!!
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
try {
// 3. 解析字符串为 Date 对象
Date date = sdf.parse(dateString);
// 4. 验证结果
System.out.println("原始字符串: " + dateString);
System.out.println("转换后的 Date 对象: " + date); // Date 对象的 toString() 会输出其内部表示
System.out.println("格式化后的 Date: " + sdf.format(date)); // 用同一个格式化器再格式化回来,验证正确性
} catch (ParseException e) {
System.err.println("日期解析失败,请检查格式是否匹配!");
e.printStackTrace();
}
}
}
常见日期格式示例
SimpleDateFormat 的模式字母非常关键:
| 字母 | 含义 | 示例 |
|---|---|---|
y |
年 | yyyy (2025), yy (23) |
M |
月 | MM (10), MMM (Oct), MMMM (October) |
d |
日 | dd (27) |
H |
小时 (24小时制) | HH (10) |
h |
小时 (12小时制) | hh (10) |
m |
分钟 | mm (30) |
s |
秒 | ss (00) |
S |
毫秒 | SSS (123) |
示例:解析不同格式的字符串
String dateStr1 = "2025/10/27"; String pattern1 = "yyyy/MM/dd"; String dateStr2 = "27-Oct-2025"; String pattern2 = "dd-MMM-yyyy"; // ... 使用 sdf.parse() 进行解析 ...
线程安全问题 (非常重要)
SimpleDateFormat 的内部状态在解析和格式化过程中会发生变化,如果在多线程环境下共享同一个 SimpleDateFormat 实例,会导致解析结果错误或 NullPointerException。

(图片来源网络,侵删)
错误示范 (多线程环境)
// 不要这样做!
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public void parseInMultiThread(String dateStr) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + ": " + sdf.parse(dateStr));
} catch (ParseException e) {
e.printStackTrace();
}
}).start();
}
解决方案 1:每次创建新实例 (性能差)
// 在方法内部创建,虽然安全,但频繁创建销毁对象会影响性能
public Date parse(String dateStr) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(dateStr);
}
解决方案 2:使用 synchronized 同步块 (性能较差)
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public synchronized Date parse(String dateStr) throws ParseException {
return sdf.parse(dateStr);
}
使用现代 API (Java 8+ java.time)
这是目前 最佳实践。java.time API 设计优秀,解决了旧 API 的所有痛点:线程安全、易用、功能强大。

(图片来源网络,侵删)
基本转换
java.time 提供了多个类来处理不同场景的日期时间,你需要根据你的字符串内容选择合适的类。
LocalDate: 仅包含日期 (年月日),如2025-10-27。LocalTime: 仅包含时间 (时分秒),如10:30:00。LocalDateTime: 包含日期和时间,但不带时区,如2025-10-27T10:30:00。ZonedDateTime: 包含日期、时间和时区,如2025-10-27T10:30:00+08:00[Asia/Shanghai]。
示例:LocalDateTime 转换
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class StringToDateModern {
public static void main(String[] args) {
// 1. 定义日期字符串和格式
String dateString = "2025-10-27 10:30:00";
String pattern = "yyyy-MM-dd HH:mm:ss";
// 2. 创建 DateTimeFormatter 对象
// DateTimeFormatter 是线程安全的,可以安全地定义为 static final 常量
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
try {
// 3. 解析字符串为 LocalDateTime 对象
LocalDateTime dateTime = LocalDateTime.parse(dateString, formatter);
// 4. 验证结果
System.out.println("原始字符串: " + dateString);
System.out.println("转换后的 LocalDateTime 对象: " + dateTime);
System.out.println("格式化后的 LocalDateTime: " + dateTime.format(formatter));
// 如果你需要 java.util.Date 对象(与旧系统交互)
// 可以先转换为 Instant,再转换为 Date
java.util.Date date = java.util.Date.from(dateTime.atZone(java.time.ZoneId.systemDefault()).toInstant());
System.out.println("转换回 java.util.Date: " + date);
} catch (Exception e) {
System.err.println("日期解析失败,请检查格式是否匹配!");
e.printStackTrace();
}
}
}
解析为 java.util.Date 的现代方式
如果你必须得到一个 java.util.Date 对象,推荐通过 Instant 中转,这是最清晰和可靠的方式。
import java.time.*;
import java.time.format.DateTimeFormatter;
public class StringToUtilDateModern {
public static void main(String[] args) {
String dateString = "2025-10-27 10:30:00";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 1. 解析为 LocalDateTime
LocalDateTime localDateTime = LocalDateTime.parse(dateString, formatter);
// 2. 获取系统默认时区
ZoneId zoneId = ZoneId.systemDefault();
// 3. 将 LocalDateTime 转换为 ZonedDateTime (加上时区信息)
ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
// 4. 将 ZonedDateTime 转换为 Instant (时间线上的一个瞬时点)
Instant instant = zonedDateTime.toInstant();
// 5. 将 Instant 转换为 java.util.Date
java.util.Date date = java.util.Date.from(instant);
System.out.println("最终得到的 java.util.Date: " + date);
}
}
总结与最佳实践
| 特性 | java.util.Date + SimpleDateFormat |
java.time API |
|---|---|---|
| 推荐度 | ⭐⭐ (仅维护旧代码时使用) | ⭐⭐⭐⭐⭐ (新项目首选) |
| 线程安全 | ❌ 不安全,需额外处理 | ✅ 安全,DateTimeFormatter 是线程安全的 |
| API 设计 | 繁琐,方法大多已过时 | 现代化,清晰易用 (如 LocalDate, LocalDateTime) |
| 时区处理 | 支持,但容易出错 | 强大且直观 (ZonedDateTime, ZoneId) |
| 不可变性 | 可变 | 不可变,更安全 |
| 性能 | 一般 | 通常更好 |
- 新项目:请务必使用
java.timeAPI,它是 Java 日期时间的未来,能让你避免很多潜在的 bug。 - 维护旧项目:如果你必须使用
java.util.Date,请务必注意SimpleDateFormat的线程安全问题,最好的做法是在解析时创建新的实例,或者使用synchronized块(但性能有损耗)。 - 与旧代码交互:当你使用
java.time解析完数据后,如果某个旧库需要java.util.Date,请通过Instant进行转换,这是最标准的方式。
