📑 Java 演进系列总览 | Java 8 | Java 11 | Java 17 | Java 21 | Java 25

Java 21 于 2023 年 9 月 19 日 GA,是继 Java 17 之后的 LTS 版本,按 Oracle 的支持路线图获得长期支持。它的核心主题是并发模型的跃迁:虚拟线程成为正式特性,让"一请求一线程"的阻塞式写法重新可行;记录模式与 switch 模式匹配在语言层面定型,与 Java 17 的 sealed、record 汇合成完整的数据导向编程范式。需要澄清的是:String Templates、Scoped Values、Structured Concurrency、Unnamed Classes 在 Java 21 中均为预览特性,不属于永久语言特性,生产使用需 --enable-preview 并评估后续版本的演进。

虚拟线程

虚拟线程(JEP 444,正式特性)是 JVM 托管的轻量级线程,由调度器挂载到载体平台线程上运行。遇到阻塞 I/O 时,虚拟线程自动让出载体线程,阻塞成本从"占用一个 OS 线程"降为"一次栈帧切换",单机可轻松支撑百万级并发任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 1; i <= 100; i++) {
int id = i;
executor.submit(() -> {
var body = fetch("https://example.com/api/" + id);
System.out.println(id + ": " + body.length());
});
}
}

// 阻塞 I/O:虚拟线程自动让出载体线程
String fetch(String url) throws Exception {
var in = new URI(url).toURL().openStream();
try (in) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
}

使用要点:虚拟线程适合阻塞式 I/O 密集任务,CPU 密集任务仍用平台线程池;避免在虚拟线程中长期持有 synchronized 锁(Java 24 的 JEP 491 才消除其钉住问题),改用 ReentrantLock;不要池化虚拟线程,直接每任务创建。深入实践参见虚拟线程专题

记录模式

记录模式(JEP 440,正式特性)允许 instanceofswitch 直接解构 record 的组件,并支持嵌套——一条模式语句即可拆出多层字段。它让 record 从"声明数据的载体"进一步成为"逻辑分支的依据"。

1
2
3
4
5
6
7
8
9
10
11
record Point(int x, int y) {}
record Line(Point start, Point end) {}

// 嵌套解构:一次模式匹配拆出两层字段
static int manhattan(Object value) {
if (value instanceof Line(
Point(var x1, var y1), Point(var x2, var y2))) {
return Math.abs(x2 - x1) + Math.abs(y2 - y1);
}
return 0;
}

模式变量(如 x1y1)在匹配成功后自动可用,替代了传统的"instanceof 判断 + 强制转型 + 逐字段取值"样板。嵌套解构是 Java 21 的关键增量:Line(Point(...), Point(...)) 在一条模式中同时匹配外层与内层。

switch 模式匹配

switch 模式匹配(JEP 441,正式特性)让 case 支持类型模式与记录模式,且当 switch 目标是 sealed 层级时编译器可做穷尽性检查——分支覆盖所有许可子类即可省略 default,新增子类时编译期立即报错。

1
2
3
4
5
6
7
8
9
10
11
sealed interface Shape permits Circle, Square {}
record Circle(double radius) implements Shape {}
record Square(double side) implements Shape {}

// sealed 层级:穷尽性由编译器保证,无需 default
static double area(Shape shape) {
return switch (shape) {
case Circle(double r) -> Math.PI * r * r;
case Square(double s) -> s * s;
};
}

case null 分支可直接处理空值而不再抛出 NullPointerExceptioncase Circle c when c.radius() > 100 这类带条件的守卫分支也是同一语法的自然组成。配合 sealed 类型,"封闭集合 + 分支穷尽"构成了类型安全的数据处理闭环。

有序集合

有序集合(JEP 431,正式特性)为"具有确定遍历顺序"的集合补齐了统一 API:SequencedCollection 及其变体提供 getFirst()getLast()reversed()addFirst()addLast()。此前获取 List 末元素要写 list.get(list.size() - 1)LinkedHashSet 两端访问更是没有标准途径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
List<String> list = new ArrayList<>(List.of("a", "b", "c"));

list.getFirst(); // "a",替代 list.get(0)
list.getLast(); // "c",替代 list.get(list.size() - 1)
list.reversed(); // [c, b, a] 的逆序视图,不复制

// LinkedHashSet 同样获得两端访问
var set = new LinkedHashSet<>(List.of("x", "y"));
set.getFirst(); // "x"
set.getLast(); // "y"

// Deque 的旧方法映射为统一接口
Deque<Integer> deque = new ArrayDeque<>(List.of(1, 2, 3));
deque.getFirst(); // 1

reversed() 返回逆序视图,对底层集合的修改会同步反映。迁移时注意接口层面的影响:自定义集合类若实现 List,会间接继承新的 default 方法,覆盖它们以保持语义一致。

外部函数与内存 API(预览)

外部函数与内存 API(FFM,JEP 443)在 Java 21 处于第三次预览,需 --enable-preview 编译运行。它以 MemorySegmentArenaLinker 替代 JNI 与 sun.misc.Unsafe:堆外内存分配有确定的生命周期(Arena 关闭即释放),访问受边界检查保护。

1
2
3
4
5
6
7
// 编译运行:java --enable-preview --source 21 FfmDemo.java
try (Arena arena = Arena.ofConfined()) {
MemorySegment segment = arena.allocate(8);
segment.set(ValueLayout.JAVA_INT, 0, 42);
int value = segment.get(ValueLayout.JAVA_INT, 0);
System.out.println(value); // 42
}
1
2
# 预览特性需显式开启
java --enable-preview --source 21 FfmDemo.java

Arena 关闭后段不可再访问,取代了 Cleaner 兜底释放的脆弱模式。该 API 直到 Java 22 才定稿,生产依赖需锁定预览语义并跟踪后续版本的签名变化。

分代 ZGC(HotSpot/JDK)

分代 ZGC(JEP 439)是 HotSpot 虚拟机的垃圾回收器实现改进,属于 JDK 发行版的运行时能力,而非 Java SE 语言或 API 保证。ZGC 引入分代假设——多数对象朝生夕死,为年轻代单独回收后,吞吐与内存占用同时改善,且保持亚毫秒级停顿的目标不变。

1
2
3
4
5
# 启用分代 ZGC
java -XX:+UseZGC -XX:+ZGenerational App

# Java 23 起分代模式成为 ZGC 默认,标志被移除
java -XX:+UseZGC App

适用于大堆、低延迟的服务端负载。是否可用取决于具体 JDK 发行版与 GC 选择,不应写入依赖 Java SE 规范的代码假设。

常用特性总结

  • 虚拟线程Executors.newVirtualThreadPerTaskExecutor() 处理阻塞 I/O 高并发,不池化、避免长持 synchronized
  • 记录模式instanceof Line(Point(var x1, var y1), ...) 嵌套解构,消除强制转型样板。
  • switch 模式匹配:sealed 层级上的类型模式分支,编译器保证穷尽性,支持 null 分支与守卫条件。
  • 有序集合getFirst()getLast()reversed() 统一有序集合两端访问。
  • FFM API(预览)Arena + MemorySegment 管理 Java 堆外内存,需 --enable-preview
  • 分代 ZGC(HotSpot/JDK)-XX:+UseZGC -XX:+ZGenerational 获得分代低延迟回收。

迁移与兼容性

从 Java 17 升级到 Java 21 通常平稳:语言与 API 变化以新增为主,默认字符集等破坏性调整已在 Java 18 完成。重点检查项:升级前确认第三方库与框架(尤其 Spring、Netty 系)声明支持 Java 21;synchronized 长期持锁的代码迁入虚拟线程前先改 ReentrantLock;实现自定义集合类时审计新增的 SequencedCollection default 方法;不建议在模块路径上携带依赖 FFM 预览签名的库,其 API 在 Java 22 后有变动。生产采用预览特性前必须接受后续版本可能不兼容的成本,String Templates 甚至在 Java 23 被移除重设计——这是"预览不等于最终特性"的直接例证。

参考资料