怎么读这一页
这一页按版本列出 JDK 21 到 25 的主要变化,重点是正式特性;预览和孵化特性单独标注,提醒你不要在生产代码里依赖。信息来源是 OpenJDK 各版本的 JEP 列表,写作时逐项核对过。版本细节以官方 Release Notes 为准,这里给的是学习与迁移视角的摘要。
先记住三条原则:
- 正式(final)才能放心用。 预览特性可能在下一个版本改名、改语义甚至删除。
- LTS 才是生产主线。 21 和 25 是 LTS;22、23、24、26、27 是特性版,支持周期短。
- 语法新不代表必须用。 团队没升级到 25,就不要在共享库里用 25 独有语法。
版本总览
| 版本 | 发布时间 | 类型 | 本书角色 |
|---|---|---|---|
| JDK 21 | 2023-09 | LTS | 对照线,编译目标默认 |
| JDK 22 | 2024-03 | 特性版 | 仅作版本演进参考 |
| JDK 23 | 2024-09 | 特性版 | 仅作版本演进参考 |
| JDK 24 | 2025-03 | 特性版 | 卷四会提到 AOT 与 GC |
| JDK 25 | 2025-09 | LTS | 主线,开发与运行版本 |
| JDK 26 | 2026-03 | 特性版 | 不展开 |
| JDK 27 | 2026-09 | 特性版 | 不展开 |
JDK 21 LTS:虚拟线程与模式匹配
JDK 21 是本书的对照线,也是很多公司的生产版本。它的正式特性主要集中在这几项:
| JEP | 特性 | 影响 |
|---|---|---|
| 431 | Sequenced Collections | List、Deque、SortedSet 有了统一的「首/尾」访问接口 |
| 439 | Generational ZGC | ZGC 支持分代,降低年轻代回收成本 |
| 440 | Record Patterns | 可以在模式里解构 record |
| 441 | Pattern Matching for switch | switch 支持类型模式和守卫 |
| 444 | Virtual Threads | 虚拟线程正式可用 |
| 452 | Key Encapsulation Mechanism API | 后量子密码迁移的基础 API |
卷一用到的 record、sealed、switch 模式匹配、文本块,在 21 里都已正式。下面这段代码在 21 上可以编译运行:
sealed interface Command permits AddBook, BorrowBook { }
record AddBook(String isbn, String title) implements Command { }
record BorrowBook(String isbn, String memberId) implements Command { }
static String describe(Command c) {
return switch (c) {
case AddBook(var isbn, var title) -> "新增 " + title;
case BorrowBook(var isbn, var memberId) -> "借出 " + isbn + " 给 " + memberId;
};
}
本机用 javac --release 21 编译并运行:
新增 Java
借出 978 给 m-1
21 的预览特性包括 String Templates(JEP 430)、Unnamed Patterns and Variables(442)、Unnamed Classes and Instance Main Methods(443)、Scoped Values(446)、Structured Concurrency(453)、FFM API 第三次预览(442)、Vector API(448)。这些在 21 上都需要 --enable-preview,不要在没有评估的情况下用于生产。
JDK 22:FFM 正式与未命名变量
JDK 22 是特性版,主要变化:
| JEP | 特性 | 状态 |
|---|---|---|
| 423 | Region Pinning for G1 | 正式 |
| 447 | Statements before super(…) | 正式 |
| 454 | Foreign Function & Memory API | 正式 |
| 456 | Unnamed Variables & Patterns | 正式 |
| 458 | Launch Multi-File Source-Code Programs | 正式 |
| 457 | Class-File API | 预览 |
| 461 | Stream Gatherers | 预览 |
| 462 | Structured Concurrency | 第二次预览 |
| 464 | Scoped Values | 第二次预览 |
对卷一有实际影响的是两项:
_未命名变量(456 正式):for (var _ : list)或catch (Exception _),表示「我不关心这个值」。21 上不可用。super(...)之前的语句(447 正式):构造器里可以先做参数校验,再调用父类构造器。21 上不可用;25 的 JEP 513 把它扩展成「灵活构造器」。
FFM API 正式意味着 Java 可以更安全地调用本地库,替代大部分 JNI 场景。卷一不涉及,卷三会讲。
JDK 23:Markdown 文档与分代 ZGC 默认
| JEP | 特性 | 状态 |
|---|---|---|
| 467 | Markdown Documentation Comments | 正式 |
| 474 | ZGC: Generational Mode by Default | 正式 |
| 471 | Deprecate sun.misc.Unsafe Memory-Access Methods | 正式(弃用) |
| 455 | Primitive Types in Patterns | 预览 |
| 473 | Stream Gatherers | 第二次预览 |
| 476 | Module Import Declarations | 预览 |
| 480 | Structured Concurrency | 第三次预览 |
| 481 | Scoped Values | 第三次预览 |
| 482 | Flexible Constructor Bodies | 第二次预览 |
两个实际影响:
- Javadoc 可以写 Markdown(467):
///开头的文档注释支持 Markdown 语法,文档更好写。 - ZGC 默认分代(474):用 ZGC 时不再需要显式开分代,低延迟场景的默认行为变了。
sun.misc.Unsafe 的内存访问方法被标记为弃用(471),这是为未来移除做准备。业务代码不应直接用 Unsafe;框架和序列化库会逐步迁移到 VarHandle 和 FFM API。
JDK 24:Class-File API、Stream Gatherers、虚拟线程去固定
| JEP | 特性 | 状态 |
|---|---|---|
| 404 | Generational Shenandoah | 实验 |
| 450 | Compact Object Headers | 实验 |
| 472 | Prepare to Restrict the Use of JNI | 正式(警告) |
| 475 | Late Barrier Expansion for G1 | 正式 |
| 479 | Remove the Windows 32-bit x86 Port | 正式 |
| 483 | Ahead-of-Time Class Loading & Linking | 正式 |
| 484 | Class-File API | 正式 |
| 485 | Stream Gatherers | 正式 |
| 486 | Permanently Disable the Security Manager | 正式 |
| 490 | ZGC: Remove the Non-Generational Mode | 正式 |
| 491 | Synchronize Virtual Threads without Pinning | 正式 |
| 493 | Linking Run-Time Images without JMODs | 正式 |
| 496 | Quantum-Resistant KEM | 正式 |
| 497 | Quantum-Resistant DSA | 正式 |
| 487 | Scoped Values | 第四次预览 |
| 488 | Primitive Types in Patterns | 第二次预览 |
| 492 | Flexible Constructor Bodies | 第三次预览 |
| 494 | Module Import Declarations | 第二次预览 |
| 495 | Simple Source Files and Instance Main Methods | 第四次预览 |
| 499 | Structured Concurrency | 第四次预览 |
对卷三、卷四最重要的是 491:虚拟线程在 synchronized 块里不再固定(pin)载体线程。 在 21 上,虚拟线程遇到 synchronized 可能会阻塞载体线程,影响吞吐;24 起这个问题基本消失。卷三讲虚拟线程时会展开。
484 的 Class-File API 正式,意味着读写字节码有了官方 API,不必依赖 ASM。卷三会用它做简单的字节码分析。485 的 Stream Gatherers 给 Stream 增加了自定义中间操作,卷一会提一句,不深入。
JDK 25 LTS:Scoped Values、紧凑源文件、灵活构造器
JDK 25 是本书主线。正式特性里,和入门书关系最大的有六项:
| JEP | 特性 | 对卷一的意义 |
|---|---|---|
| 506 | Scoped Values | 线程间共享不可变上下文,替代部分 ThreadLocal |
| 510 | Key Derivation Function API | 密码学 API,卷一不涉及 |
| 511 | Module Import Declarations | import module java.base; 一行导入整个模块 |
| 512 | Compact Source Files and Instance Main Methods | 小例子可以不写 class 和 public static void main |
| 513 | Flexible Constructor Bodies | 构造器里先校验、再调 super(...) |
| 519 | Compact Object Headers | 对象头从 96/128 位压到 64 位,降低内存 |
| 503 | Remove the 32-bit x86 Port | 32 位 x86 不再支持 |
| 514 | Ahead-of-Time Command-Line Ergonomics | AOT 缓存更易用 |
| 515 | Ahead-of-Time Method Profiling | AOT 方法分析 |
| 518 | JFR Cooperative Sampling | JFR 采样改进 |
| 520 | JFR Method Timing & Tracing | JFR 方法级计时 |
| 521 | Generational Shenandoah | Shenandoah 分代 |
模块导入声明
JDK 25 起,可以一行导入整个模块的公开包:
import module java.base;
public class ModuleImport {
public static void main(String[] args) {
List<String> names = List.of("a", "b");
System.out.println(names);
}
}
本机输出:
[a, b]
这减少了大量 import java.util.* 之类的样板,但也会让「这个名字来自哪个包」变得不直观。团队项目里是否采用,需要权衡可读性。
紧凑源文件与实例 main
JDK 25 起,小例子可以写成:
void main() {
System.out.println("compact source file: " + Runtime.version());
}
用 java Hello.java 直接运行(文件名不必与类名一致,因为没有显式类):
compact source file: 25.0.5+8-LTS
这适合教学、脚本和原型;正式项目仍然建议写完整的类结构,否则包、模块、可见性都无从谈起。
灵活构造器
JDK 25 起,构造器里可以先做校验,再调用 super(...):
public class Isbn {
private final String value;
public Isbn(String raw) {
if (raw == null || raw.isBlank()) {
throw new IllegalArgumentException("isbn");
}
super();
this.value = raw;
}
}
本机运行:
978-7-111-12345-6
isbn
21 上同样的代码无法编译,因为语句不能出现在 super() 之前。这个特性让「参数校验」和「父类构造」的顺序更自然,也避免了在父类构造器里调用可被重写的方法。
Scoped Values
Scoped Values 提供一种线程内(以及虚拟线程间)共享不可变上下文的方式:
private static final ScopedValue<String> MEMBER_ID = ScopedValue.newInstance();
ScopedValue.where(MEMBER_ID, "m-1").run(() -> {
System.out.println(MEMBER_ID.get());
});
它比 ThreadLocal 更安全:值不可变、作用域明确、虚拟线程下没有继承开销。卷三会详细讲它和 ThreadLocal 的取舍。
紧凑对象头
JEP 519 把对象头从 96 位(或 128 位)压缩到 64 位,减少堆占用。这是 JVM 层的优化,对代码透明,但会影响对象布局、jol 等工具的读数。卷四讲对象布局时会提到。
迁移建议
从 JDK 21 升到 25,按这个顺序做:
- 先升构建工具链。 Maven Compiler Plugin、Surefire、Failsafe、Shade、SpotBugs、JaCoCo 都要支持 25。
- 检查依赖。 字节码增强类库(Mockito、Byte Buddy、CGLIB)、序列化库、数据库驱动、容器基础镜像是否声明支持 25。
- 跑测试。 重点看并发、日期时间、字符集、反射、
Unsafe相关测试。 - 看 GC 日志和启动时间。 25 的默认 GC、紧凑对象头、AOT 缓存都可能改变性能曲线。
- 再改代码。 用新语法是最后一步,不要和升级混在一起。
不要做的事:
- 不要在生产代码里用预览特性,除非你接受每个版本都要改。
- 不要用
-source/-target代替--release。 - 不要假设「21 能跑,25 一定行为一样」,尤其是 GC、字符集、TLS、日期时间默认值。
特性矩阵
| 特性 | 21 | 22 | 23 | 24 | 25 |
|---|---|---|---|---|---|
| record / sealed | ✅ | ✅ | ✅ | ✅ | ✅ |
| switch 模式匹配 | ✅ 正式 | ✅ | ✅ | ✅ | ✅ |
| 虚拟线程 | ✅ 正式 | ✅ | ✅ | ✅ | ✅ |
未命名变量 _ | ❌ 预览 | ✅ 正式 | ✅ | ✅ | ✅ |
| FFM API | 预览 | ✅ 正式 | ✅ | ✅ | ✅ |
| Markdown 文档注释 | ❌ | ❌ | ✅ 正式 | ✅ | ✅ |
| Class-File API | ❌ | 预览 | 预览 | ✅ 正式 | ✅ |
| Stream Gatherers | ❌ | 预览 | 预览 | ✅ 正式 | ✅ |
| 虚拟线程去固定 | ❌ | ❌ | ❌ | ✅ 正式 | ✅ |
| 模块导入声明 | ❌ | ❌ | 预览 | 预览 | ✅ 正式 |
| 紧凑源文件 / 实例 main | ❌ 预览 | 预览 | 预览 | 预览 | ✅ 正式 |
| 灵活构造器 | ❌ | ❌ | 预览 | 预览 | ✅ 正式 |
| Scoped Values | 预览 | 预览 | 预览 | 预览 | ✅ 正式 |
| 紧凑对象头 | ❌ | ❌ | ❌ | 实验 | ✅ 正式 |
| 结构化并发 | 预览 | 预览 | 预览 | 预览 | 第五次预览 |
「预览」表示需要 --enable-preview,且 API 可能在下一版变化;「❌」表示该版本没有这个特性。
常见误区
「25 是 LTS,所以所有库都支持。」 LTS 是 JDK 的支持承诺,不是第三方库的承诺。升级前查依赖的官方兼容性说明。
「预览特性用着没问题,就可以上生产。」 预览特性可能在下一个版本删除或改语义。除非你能接受每个版本跟着改,否则不要用。
「新语法一定更好。」 紧凑源文件适合教学和脚本,不适合正式项目;模块导入声明减少样板,但降低可读性。语法选择要看团队和维护成本。
「21 和 25 只是版本号不同。」 虚拟线程去固定、ZGC 默认分代、紧凑对象头、Unsafe 弃用,这些都会改变运行时行为。升级要按迁移清单走。
小结
- 21 的正式特性:Sequenced Collections、分代 ZGC、记录模式、switch 模式匹配、虚拟线程。
- 22 的正式特性:FFM API、未命名变量、
super(...)前语句、多文件源码启动。 - 23 的正式特性:Markdown 文档注释、ZGC 默认分代、
Unsafe内存访问弃用。 - 24 的正式特性:Class-File API、Stream Gatherers、虚拟线程去固定、AOT 类加载、后量子密码 API。
- 25 的正式特性:Scoped Values、模块导入声明、紧凑源文件、灵活构造器、紧凑对象头、JFR 改进。
- 卷一以 25 为主线、21 为对照;编译目标默认 21,语法只用 21 也成立的部分。
- 迁移顺序:工具链 → 依赖 → 测试 → 观测 → 再改代码。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。