JDK 21 一页速查卡
版本与边界
3先确认 class 版本
java -version
javac -version
javap -verbose App | grep major
JDK 8/11/17/21/25 对应 52/55/61/65/69。预览编译的 class 是 65.65535,必须由同一版本运行。
预览特性两端都要开
javac --release 21 --enable-preview App.java
java --enable-preview App
只开一端会启动失败;预览 class 不能跨 JDK 版本运行,升级后必须重新编译。
21 的正式与预览
- 正式:虚拟线程、switch 模式匹配、record 模式、SequencedCollection、分代 ZGC、KEM
- 预览:结构化并发、Scoped Values、FFM;String Templates 在 23 撤回
--release 21可限制 API;高于当前 JDK 的 release 会直接报错
语言表达力
3数据类 + 校验
record Range(int lo, int hi) {
Range {
if (lo > hi) throw new IllegalArgumentException();
}
}
紧凑构造器不写参数列表;字段自动 private final,并生成访问器、equals、hashCode、toString。
受控继承 + 穷尽 switch
sealed interface Shape permits Circle, Square {}
record Circle(double r) implements Shape {}
record Square(double a) implements Shape {}
double area(Shape s) {
return switch (s) {
case Circle(double r) -> Math.PI * r * r;
case Square(double a) -> a * a;
};
}
sealed 的子类必须 final / sealed / non-sealed;switch 不写 default 也能穷尽。
null 与守卫
return switch (obj) {
case null -> "null";
case String s when s.isBlank() -> "blank";
case String s -> "string: " + s;
default -> "other";
};
when 是模式守卫;case null 必须显式写,否则 switch 遇到 null 抛 NPE。
并发模型
3每任务一线程
try (var ex = Executors.newVirtualThreadPerTaskExecutor()) {
Future<String> f = ex.submit(() -> fetch());
System.out.println(f.get());
}
不要池化虚拟线程;它提升吞吐与并发量,不加速 CPU 密集任务。JDK 21 在 synchronized 内阻塞会 pinning,24 才修复(JEP 491)。
StructuredTaskScope(预览)
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var a = scope.fork(() -> fetchA());
var b = scope.fork(() -> fetchB());
scope.join().throwIfFailed();
return a.get() + b.get();
}
21 需要 --enable-preview;join / throwIfFailed 的受检异常要声明。子任务失败会取消兄弟任务。
ScopedValue(预览)
static final ScopedValue<String> USER = ScopedValue.newInstance();
ScopedValue.where(USER, "alice").run(() -> {
System.out.println(USER.get());
});
不可变、自动恢复;只有 StructuredTaskScope.fork 继承绑定,手工 Thread.ofVirtual().start 不继承。
集合与流
3首尾统一
List<Integer> list = new ArrayList<>(List.of(1, 2, 3));
list.getFirst(); // 1
list.getLast(); // 3
list.reversed(); // 视图,不是拷贝
List、Deque、LinkedHashSet、LinkedHashMap 都接入;对 reversed() 的修改会反映到原集合。
拷贝与工厂
List<String> a = List.copyOf(src); // 拒绝 null
Set<String> b = Set.copyOf(src);
Map<String, Integer> c = Map.of("a", 1);
List.of / Map.of 都不接受 null;需要可变时用 new ArrayList<>(...)。
默认值与合并
map.computeIfAbsent(k, key -> new ArrayList<>()).add(v);
map.merge(k, 1, Integer::sum);
map.getOrDefault(k, 0);
stream.toList(); // 不可变,JDK 16+
Stream.toList() 返回不可变列表;要可变用 collect(Collectors.toList())。
核心 API
3JDK 21 新方法
s.indexOf("bc", 2, 6); // 区间查找,返回绝对下标
"a,b;c".splitWithDelimiters("[,;]", 0);
sb.repeat("-", 3);
Math.clamp(v, 0, 100);
区间版只有 indexOf,没有 lastIndexOf;clamp 的 NaN 原样返回,min > max 抛 IAE。
Files 一行流
String text = Files.readString(path); // 默认 UTF-8
Files.writeString(path, text);
try (var lines = Files.lines(path)) { /* ... */ }
Files.lines / walk / list 必须关闭;小文件才用 readString。JDK 18 起默认 UTF-8,仍建议显式指定。
HttpClient
try (var client = HttpClient.newHttpClient()) {
var req = HttpRequest.newBuilder(uri).GET().build();
var res = client.send(req,
HttpResponse.BodyHandlers.ofString());
}
线程安全、可复用;长生命周期服务别每请求 new。JDK 21 最高支持 HTTP/2。
迁移与排障
3先跑依赖检查
jdeps --jdk-internals app.jar
jdeps --multi-release 21 app.jar
java --add-opens java.base/java.lang=ALL-UNNAMED ...
先升级依赖,再按需 --add-opens;不要用 --illegal-access 兜底(21 已忽略该参数)。
这些参数会拒绝启动
-XX:+UseConcMarkSweepGC
-XX:MaxPermSize=256m
-XX:+UseBiasedLocking
21 上都是无法识别的 VM 选项;PrintGCDetails 会警告并映射到 -Xlog。
移除与禁用
Thread.stop/suspend/resume抛UnsupportedOperationExceptionThread.countStackFrames仍在,但同样抛 UOE;改用getStackTrace/StackWalkerSecurityManager21 默认禁用,24 永久移除(JEP 486)- 生产代码隔离预览特性:class 不能跨版本,升级时必须重编译
6 个最容易记错的点TRAPS
编译和运行都要 --enable-preview,class 的 minor 是 65535;换 JDK 必须重新编译,不能直接跑。
它提升吞吐与线程数量,不加速 CPU 密集任务;池化虚拟线程没有意义。JDK 21 的 synchronized 仍会 pinning。
结构化子任务继承绑定;手工 new Thread 或 Thread.ofVirtual().start 不继承。21 的 runWhere 等旧 API 在 25 已移除。
21 用 allocateUtf8String/getUtf8String,22+ 改为 allocateFrom/getString;21 仍是预览,需要 --enable-preview。
越界抛 StringIndexOutOfBoundsException,不是 -1;没有区间版 lastIndexOf。Math.clamp 在 min > max 时抛 IAE。
reversed() 是视图,原集合变化会反映;List.copyOf 是不可变拷贝,且拒绝 null。
继续深入NEXT
内容基于本机 JDK 21.0.12 编译/运行验证;跨版本差异另用 JDK 17 / 24 / 25 核对。