【Java数据类型】Java底层数据类型原理(下):时间API、JSON与String存储演进

作者介绍:大家好,我是 CodeStats。一个在底层技术上“考古”了四年的硬核爱好者,也是 WWAIC(全周项目AI编程)范式的提出者和实践者。我曾手写过一个完整的 Java Web 框架(从 IoC 容器到嵌入式 Tomcat,代码全开源),也喜欢用通俗的语言拆解 CPU、JVM、操作系统的运行本质。我的技术信条:所有高深的技术,最后都能用大白话讲清楚。如果讲不清楚,说明还没真正理解。


本文能获得什么

  • 掌握新旧时间 API 的底层差异与实际应用Date vs InstantCalendar vs LocalDateTime,以及为什么新 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
number123-1.51e10Integer/Long/Double/BigDecimal
booleantruefalseBoolean
nullnullnull

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 将 StringStringBuilderStringBuffer 的内部字符存储从 UTF-16 char 数组改为 byte 数组加一个字节的编码标志字段。

java

public final class String {
    private final byte[] value;
    private final byte coder;  // 0 = LATIN1, 1 = UTF16
}

coder 决定存储模式

coder模式每字符适用
0LATIN11 字节码点 ≤ 255
1UTF162 字节有字符 > 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 优点

  1. 省内存:大多数 Java 程序里字符串大量是 ASCII(日志、URL、JSON key),LATIN1 模式节省一半内存。

  2. 性能提升:内存占用小,缓存友好,GC 压力小。

  3. 保持兼容:对外行为完全一致,length()charAt() 等方法语义不变。

  4. 可关闭:通过 -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 数据类型设计的每一个决策。


👍 点赞 · ⭐ 收藏 · 🔔 关注

如果本文对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续输出底层干货的最大动力!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeStats

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值