作者介绍:大家好,我是 CodeStats。一个在底层技术上“考古”了四年的硬核爱好者,也是 WWAIC(全周项目AI编程)范式的提出者和实践者。我曾手写过一个完整的 Java Web 框架(从 IoC 容器到嵌入式 Tomcat,代码全开源),也喜欢用通俗的语言拆解 CPU、JVM、操作系统的运行本质。我的技术信条:所有高深的技术,最后都能用大白话讲清楚。如果讲不清楚,说明还没真正理解。
本文能获得什么
-
掌握新旧时间 API 的底层差异与实际应用:
DatevsInstant、CalendarvsLocalDateTime,以及为什么新 API 更好 -
理解 JSON 数据类型与 Java 类型的映射规则:JSON 只有 6 种类型,时间类型如何处理
-
掌握 Jackson 时间序列化的配置方法:默认行为是什么、如何全局配置、如何用注解精确控制
-
理解 Java 9 String 底层从 char[] 改成 byte[] 的根本原因:Compact Strings 的 coder 机制与内存节省效果
目录
-
提问四:Java 的新旧时间 API 底层与差异和应用常见示例
-
提问五:Java 的 JSON 格式有哪些数据类型,对应时间类型是如何互相转换的
-
提问六:Java 的 JSON 工具是如何配置实现时间类型转换,默认会显示什么
-
提问七:Java 的 String 类底层存储为什么要从 char[] 数组改成 byte[],底层原因和优点
提问四:Java 的新旧时间 API 底层与差异和应用常见示例
4.1 旧 API:Date 和 Calendar
java.util.Date 内部只有一个 long fastTime,表示自 1970-01-01T00:00:00 UTC 以来的毫秒数。
问题:
-
可变,线程不安全
-
月份从 0 开始(0 = 一月),反直觉
-
SimpleDateFormat不是线程安全的 -
没有时区处理,容易混淆
4.2 新 API:java.time(JDK 8+)
JSR 310 引入的新 API,核心设计理念是不可变、线程安全、职责分离。
| 类 | 用途 | 精度 |
|---|---|---|
Instant | 时间戳,机器视角 | 纳秒 |
LocalDate | 日期,无时间无时区 | 天 |
LocalTime | 时间,无日期无时区 | 纳秒 |
LocalDateTime | 日期 + 时间,无时区 | 纳秒 |
ZonedDateTime | 带时区的日期时间 | 纳秒 |
Duration | 时间段(秒 + 纳秒) | 纳秒 |
Period | 日期段(年 + 月 + 日) | 天 |
Instant 使用纳秒精度,而 Date 只使用毫秒精度。
4.3 常见示例
java
// 创建
Instant now = Instant.now();
LocalDate today = LocalDate.now();
LocalDateTime ldt = LocalDateTime.of(2026, 9, 10, 12, 0, 0);
// 加减
LocalDate tomorrow = today.plusDays(1);
Instant later = now.plusSeconds(3600);
// 转换
Instant instant = ldt.atZone(ZoneId.systemDefault()).toInstant();
LocalDateTime fromInstant = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
// 格式化
String formatted = ldt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
4.4 新旧 API 对比
| 对比项 | 旧 API | 新 API |
|---|---|---|
| 可变性 | 可变 | 不可变 |
| 线程安全 | SimpleDateFormat 不安全 | DateTimeFormatter 安全 |
| 精度 | 毫秒 | 纳秒 |
| 月份 | 0 开始 | 1 开始 |
| 时区 | 混乱 | 清晰分离 |
| 职责 | Date 混在一起 | 日期、时间、时区分开 |
提问五:Java 的 JSON 格式有哪些数据类型,对应时间类型是如何互相转换的
5.1 JSON 只有 6 种类型
| JSON 类型 | 示例 | Java 常见映射 |
|---|---|---|
| object | {"name":"Tom"} | Map 或 POJO |
| array | [1,"a",true] | List 或数组 |
| string | "hello" | String |
| number | 123、-1.5、1e10 | Integer/Long/Double/BigDecimal |
| boolean | true、false | Boolean |
| null | null | null |
JSON 没有日期、时间、时间戳类型。
5.2 JSON 中时间的表示方式
| 方式 | 示例 | 说明 |
|---|---|---|
| ISO 8601 字符串 | "2026-09-10T12:00:00Z" | 推荐,可读性好 |
| 时间戳数字 | 1757486400000 | 毫秒时间戳 |
| 时间戳字符串 | "1757486400000" | 避免大数精度问题 |
| 自定义格式 | "2026-09-10 12:00:00" | 需要指定格式 |
5.3 JSON 时间 → Java 时间
java
// ISO 8601 → LocalDateTime
LocalDateTime ldt = LocalDateTime.parse("2026-09-10T12:00:00");
// 时间戳 → Instant
Instant instant = Instant.ofEpochMilli(1757486400000L);
// 字符串 → Date(旧 API)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse("2026-09-10 12:00:00");
5.4 Java 时间 → JSON 时间
java
// LocalDateTime → ISO 8601 String json = "\"" + ldt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + "\""; // Instant → 时间戳 long timestamp = instant.toEpochMilli();
提问六:Java 的 JSON 工具是如何配置实现时间类型转换,默认会显示什么
6.1 Jackson 默认行为
Jackson 解析 Date 类型数据时,默认格式是 UTC 类型(yyyy-MM-dd'T'HH:mm:ss.SSS),默认时区为 +8 时区。
序列化 Date 时,默认返回时间戳格式:
java
ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(new Date()); // 输出:1644327387386(时间戳)
6.2 关闭时间戳,改为 ISO 格式
java
ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); String json = mapper.writeValueAsString(new Date()); // 输出:"2022-02-08T14:56:27.386+00:00"
6.3 处理 Java 8 时间类型
需要注册 JavaTimeModule:
java
ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
JavaTimeModule 将 java.time 类型序列化为数字(整数或小数),启用时间戳特性时,时间戳写为秒和分数秒。
6.4 全局配置 LocalDateTime 格式
java
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
module.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
注意:全局的 spring.jackson.date-format 属性不适用于 LocalDateTime 等 Java 8 类型,需要单独配置序列化器。
6.5 注解方式
java
public class User {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime createTime;
}
6.6 各工具默认行为汇总
| 工具 | Date 默认 | LocalDateTime 默认 |
|---|---|---|
| Jackson | 时间戳 | 需要 JavaTimeModule,否则报错 |
| Gson | 格式化字符串 | 需要自定义适配器 |
| Fastjson | 时间戳或格式化 | 时间戳或格式化 |
提问七:Java 的 String 类底层存储为什么要从 char[] 数组改成 byte[],底层原因和优点
7.1 Java 8:String 内部是 char[]
java
public final class String {
private final char[] value;
}
char 是 16 位,每个字符至少占 2 字节。纯 ASCII 字符串(如 "hello")明明每个字符只要 1 字节,却要占 10 字节。
7.2 Java 9+:String 内部是 byte[] + coder
JEP 254 将 String、StringBuilder、StringBuffer 的内部字符存储从 UTF-16 char 数组改为 byte 数组加一个字节的编码标志字段。
java
public final class String {
private final byte[] value;
private final byte coder; // 0 = LATIN1, 1 = UTF16
}
coder 决定存储模式:
| coder | 模式 | 每字符 | 适用 |
|---|---|---|---|
| 0 | LATIN1 | 1 字节 | 码点 ≤ 255 |
| 1 | UTF16 | 2 字节 | 有字符 > 255 |
当字符串所有字符都可用单字节 Latin-1 表示时,内部使用 byte[] 存储;否则使用 UTF-16 双字节存储。
7.3 示例
java
String s1 = "abc"; // coder=0, value=[0x61,0x62,0x63],3 字节 String s2 = "中"; // coder=1, value=[0x4E,0x2D],2 字节 String s3 = "a中"; // coder=1, value=[0x00,0x61,0x4E,0x2D],4 字节
只要有一个字符超出 Latin-1,整个字符串切到 UTF16。
7.4 优点
-
省内存:大多数 Java 程序里字符串大量是 ASCII(日志、URL、JSON key),LATIN1 模式节省一半内存。
-
性能提升:内存占用小,缓存友好,GC 压力小。
-
保持兼容:对外行为完全一致,
length()、charAt()等方法语义不变。 -
可关闭:通过
-XX:-CompactStrings参数可关闭此功能。
7.5 代价
-
多了一个
coder字段 -
charAt等操作需要判断模式 -
JIT 会优化,性能影响很小
总结
下篇聚焦 Java 时间 API 的演进、JSON 类型映射与时间序列化配置,以及 String 底层从 char[] 到 byte[] 的存储优化。整条链条的规律可以归纳为:
text
比特 ↓ 定长数字(补码 / IEEE 754) ↓ 字符 = 数字 + 编码表 ↓ 字符串 = 字符序列(内部 LATIN1 或 UTF16) ↓ 封装类 = 数字 + 方法 + 缓存 ↓ BigInteger = int[] + signum BigDecimal = BigInteger + scale ↓ 时间 = long 时间戳 + int/short/byte 字段 + 历法 ↓ JSON 六种类型 ← 映射 → Java 类型
所有转换的本质只有几类:补码截断扩展、移位掩码、除基取余乘基加位、查编码表、大整数数组运算、历法规则运算。理解了这些底层规律,你就能看透 Java 数据类型设计的每一个决策。
👍 点赞 · ⭐ 收藏 · 🔔 关注
如果本文对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续输出底层干货的最大动力!
:时间API、JSON与String存储演进&spm=1001.2101.3001.5002&articleId=164867991&d=1&t=3&u=cfb71b70a2184bb8bd667bdd90f5875c)
387

被折叠的 条评论
为什么被折叠?



