JDK 25 一页速查卡
版本与边界
325 的正式、预览与产品默认
- 正式:506 Scoped Values、511 模块导入、512 紧凑源文件/实例 main、513 灵活构造器体、514/515 AOT、510 KDF、503 移除 32 位 x86
- 预览:505 结构化并发(第五次)、502 Stable Values、507 原始类型模式、470 PEM、508 Vector API
- 产品但默认关闭:519 紧凑对象头、521 分代 Shenandoah
- 仍是 LTS:25 于 2025-09-16 发布,class 文件版本 69
「转正」是支持级别,不是默认值;「预览」是 API 仍可能改,不是 beta 正式版。
class 版本与预览参数
javac --release 25 --enable-preview App.java
java --enable-preview App
javap -verbose App | grep major
25 的 class 是 69;预览 class 是 69.65535,只能由 25 运行。正式特性不要加 --enable-preview;21 的预览 class 是 65.65535,25 不认。
21 → 25 先查这六项
- 是否仍依赖 32 位 x86;该端口已在 25 移除
- 是否还在用 Security Manager 或 Thread.stop(25 抛 UOE)
- 预览 class 是否随 JDK 升级重新编译
- 结构化并发是否仍按 21 的 ShutdownOnFailure 写
- ScopedValue 是否还在用 runWhere / callWhere / getWhere
- 紧凑对象头 / 分代 Shenandoah 是否被误认为默认开启
升级顺序:先编译通过,再跑测试,再做运行时开关 A/B,最后切默认与灰度。
并发
3请求上下文只读绑定
static final ScopedValue<User> USER = ScopedValue.newInstance();
ScopedValue.where(USER, user)
.where(TRACE_ID, req.traceId())
.run(() -> service.handle(req));
嵌套遮蔽、异常/结束自动恢复。结构化 fork 继承;手工线程与线程池不继承。没有 set/remove。
open + Joiner(25 预览)
try (var scope = StructuredTaskScope.open(
StructuredTaskScope.Joiner.<String>allSuccessfulOrThrow())) {
var a = scope.fork(() -> "a");
var b = scope.fork(() -> "b");
scope.join().forEach(s -> IO.println(s.get()));
}
21 的 ShutdownOnFailure / ShutdownOnSuccess 已移除。编译和运行都要 --enable-preview,且必须是同一个 JDK 25。
超时与 close 的边界
try (var scope = StructuredTaskScope.open(
StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow(),
cfg -> cfg.withTimeout(java.time.Duration.ofMillis(100)))) {
scope.fork(() -> callRemote());
return scope.join();
} catch (StructuredTaskScope.TimeoutException e) {
throw new RuntimeException("timeout", e);
}
超时是 TimeoutException,不是 FailedException。close 会取消并等待;中断是协作式的,吞中断会让 close 阻塞。
语言与模块
3没有 class 外壳
void main() {
IO.println("hello, jdk 25");
}
java Hello.java 在内存中编译运行;不能有 package;IO 静态方法不再隐式导入,要写 IO.println。隐式类 final、包私有。
一行导入一个模块
import module java.base;
void main() {
IO.println(List.of("a", "b"));
}
导入模块导出的包 + transitive requires 的导出包;不替代 requires,不改变运行时模块图,不能导入 classpath 未命名模块。
super() 之前先校验
Employee(int age) {
if (age < 18) {
throw new IllegalArgumentException("too young");
}
super(age);
}
可以校验参数、给无 初始化器 字段赋值;不能读字段、用 this、调实例方法、在 prologue return。有 初始化器 的字段不能提前赋值。
运行时与迁移
364 位头,默认关闭
java -XX:+PrintFlagsFinal -version | grep UseCompactObjectHeaders
java -XX:+UseCompactObjectHeaders -jar app.jar
25 产品特性,默认 false。旧布局 96/128 位 → 64 位。依赖压缩类指针;关闭 UseCompressedClassPointers 会警告并禁用紧凑头。
产品特性,默认 satb
java -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -jar app.jar
JEP 521 只是把分代模式转为产品,默认仍是 satb。必须先选 ShenandoahGC,再显式指定 generational;不是默认 GC。
record → create → run
java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconf -cp app.jar App
java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconf -XX:AOTCache=app.aot -cp app.jar
java -XX:AOTCache=app.aot -cp app.jar App
JEP 514/515。改善启动与预热,不保证峰值吞吐;训练运行要覆盖真实路径,缓存与 JDK/平台绑定。
4 个最容易记错的点TRAPS
紧凑对象头(JEP 519)与分代 Shenandoah(JEP 521)在 25 都是产品特性,但默认分别是 false 和 satb。必须显式开启,并用 PrintFlagsFinal / GC 日志确认。
结构化并发在 25 是第五次预览(JEP 505),不是正式。编译和运行都要 --enable-preview,且 21 的 ShutdownOnFailure 代码不能直接编译。
import module 只是编译期的按需包导入,不添加 requires、不改变模块图、不打开封装,也不能导入 classpath 上的未命名模块。
预览 class 的 minor 是 65535,只对编译它的 JDK 版本有效。21 的 65.65535 在 25 上抛 UnsupportedClassVersionError;升级后必须重新编译。
继续深入NEXT
内容基于本机 JDK 25(Corretto 25+36-LTS)编译/运行验证;预览 API 单独标注 --enable-preview,JEP 状态按正式定稿情况标注。