String 与日期时间
约 1077 字大约 4 分钟
布欧-Lewyon
2026-05-14
首页 › Java › 核心类库 › String 与日期时间
本节覆盖日常编码中使用频率最高的两类 API:String / StringBuilder 的常见操作,以及 Java 8 引入的 java.time 日期时间库。
不可变字符串 String
String 是 Java 中使用频率最高的引用类型之一。它最关键的设计决策是不可变性——一旦创建,字符串内部的字符序列不会改变。任何看似修改的操作(如 toUpperCase、replace)都会在堆上创建新的 String 对象,原字符串不受影响。不可变意味着 String 可以安全地在多线程间共享,也可用作 HashMap 的键。
以下演示 String 的常用 API。注意每个方法都没有修改 s 本身,而是返回新字符串:
String s = " Hello, Java World! ";
s.length(); // 21
s.isEmpty(); // false
s.charAt(0); // ' '
s.substring(2, 7); // "Hello"
s.indexOf("Java"); // 8
s.contains("Java"); // true
s.startsWith(" He"); // true
s.toLowerCase(); // " hello, java world! "
s.trim(); // "Hello, Java World!"
s.replace("Java", "JDK"); // " Hello, JDK World! "
s.split(","); // [" Hello", " Java World! "]substring(beginIndex, endIndex):左闭右开,endIndex指向的是结束位置之后的索引。split参数是正则表达式,特殊字符需转义:"."→"\\."。- 大量拼接不要用
+循环,改用StringBuilder(见下文)。
空字符串与 null 的区别
String a = ""; // 长度为 0 的 String 对象
String b = null; // 没有对象
a.isEmpty(); // true
// b.isEmpty(); // NullPointerException!判空时用 "".equals(str) 或 str != null && !str.isEmpty(),或工具方法 org.apache.commons.lang3.StringUtils.isNotEmpty()。
StringBuilder
StringBuilder 是可变的字符串序列,适合在循环或多次拼接的场景下避免产生大量临时对象:
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(", ");
sb.append("World");
sb.append("!");
String result = sb.toString(); // "Hello, World!"StringBuffer是线程安全版但性能更差,单线程场景始终用StringBuilder。- 预知最终大小时可通过构造器设定初始容量减少扩容:
new StringBuilder(1024)。
反例(循环中多次创建 String 对象):
// 不推荐
String s = "";
for (int i = 0; i < 1000; i++) {
s += i; // 每次循环产生新 String 对象
}// 推荐
StringBuilder sb = new StringBuilder(4000);
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
String s = sb.toString();java.time 包(Java 8+)
旧版 java.util.Date 与 java.util.Calendar 设计混乱、线程不安全。新项目应全部使用 java.time。
常用类型
| 类型 | 含义 | 示例 |
|---|---|---|
LocalDate | 日期(不含时间与时区) | 2026-05-14 |
LocalTime | 时间(不含日期与时区) | 14:30:00 |
LocalDateTime | 日期 + 时间(不含时区) | 2026-05-14T14:30:00 |
ZonedDateTime | 日期 + 时间 + 时区 | 2026-05-14T14:30:00+08:00[Asia/Shanghai] |
Instant | 时间戳(自 epoch 的纳秒数) | 适合跨时区时间传递 |
Duration | 时间段(秒/纳秒精度) | |
Period | 日期间隔(年月日) |
基本用法
// 获取当前时间
LocalDate today = LocalDate.now();
LocalDateTime now = LocalDateTime.now();
// 指定日期
LocalDate date = LocalDate.of(2026, 5, 14);
LocalTime time = LocalTime.of(14, 30, 0);
// 解析
LocalDate parsed = LocalDate.parse("2026-05-14"); // 默认 ISO-8601
LocalDateTime dt = LocalDateTime.parse("2026-05-14T14:30:00");
// 格式化
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String formatted = today.format(fmt); // "2026/05/14"运算与比较
LocalDate tomorrow = today.plusDays(1);
LocalDate lastMonth = today.minusMonths(1);
boolean before = date.isBefore(today);
long daysBetween = ChronoUnit.DAYS.between(date, today);Instant 与时间戳互转
Instant now = Instant.now(); // UTC 瞬时
long epochSecond = now.getEpochSecond(); // 秒
Date legacy = Date.from(now); // 与旧 API 互转
Instant fromLegacy = legacy.toInstant();注意时区陷阱
LocalDateTime不含时区信息,直接存储或传输到不同时区的机器时可能导致时间偏移误解。- 跨时区业务优先使用
Instant或ZonedDateTime,序列化为 ISO-8601 格式(如2026-05-14T06:30:00Z)。
小结
String不可变,常用 API:substring(左闭右开)、split(正则参数需转义特殊字符)、trim、replace等。StringvsStringBuilder:不可变 vs 可变;大量拼接用StringBuilder(单线程),避免+循环。java.time是 Java 8 引入的现代日期 API,优先于java.util.Date/Calendar。- 常用类型:
LocalDate(日期)、LocalDateTime(日期+时间)、Instant(时间戳)。 - 格式化用
DateTimeFormatter(线程安全),跨时区用Instant或ZonedDateTime。 - 易错:
LocalDateTime不含时区信息,跨时区序列化/传输时可能被错误解读。反例:北京时间2026-05-14T14:30:00存储到数据库后,在 UTC 时区的服务器上读出仍是14:30,而非预期的06:30UTC。 - 思考任务:获取当前
LocalDate,计算 100 天后的日期并格式化输出为yyyy年MM月dd日格式。
上一节:枚举
下一节:正则表达式
