【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
number 123-1.51e10 Integer/Long/Double/BigDecimal
boolean truefalse 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);

JavaTimeModulejava.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 模式 每字符 适用
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 优点

  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 数据类型设计的每一个决策。


👍 点赞 · ⭐ 收藏 · 🔔 关注

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

相关推荐
Co_Hui1 小时前
Java Synchronized 关键字
java
lhldsg1 小时前
折扣卡CPS小程序定制全流程实战指南:从需求分析到上线部署
java·前端·小程序
她的男孩1 小时前
多租户隔离怎么落地?拆完这1600行Starter源码,我把5个坑全踩明白了
java·后端·架构
ly76891 小时前
Spring 事件机制在微服务中实现最终一致性的设计
java·spring·微服务·异步处理·最终一致性·spring事件·事务事件
Brilliantwxx1 小时前
【STM32 】把 printf 搬到串口上 —— C 标准 IO 函数重定向与多文件工程搭建(实战串口电灯)
c语言·开发语言·stm32·单片机·嵌入式硬件·架构·ecmascript
wuminyu1 小时前
Kafka利用sendfile与Page Cache实现高性能传输剖析
java·linux·c语言·jvm·c++
我命由我123451 小时前
Android 开发问题:android.permission.CAMERA...duplicated with element declared at
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
流浪0011 小时前
大厂 C/C++ 2023–2025 面试答案题汇总
开发语言·c++
Yanjun2i1 小时前
Agent学习记录六:Tool 类 + Tool Registry
开发语言·python·学习