JDK JDK 学习手册8 → 27 · JDK 专用学习站
CHEATSHEET · JDK 21

JDK 21 一页速查卡

JDK 21 · 6 个分区 · 18 张卡片 · A4 打印优化
返回速查模块
怎么用6 个分区、18 张卡片,把 JDK 21 最常用的写法与版本边界压缩到一张可打印的页面。适合贴在显示器旁,或面试前十分钟扫一遍。
6 个分区18 张卡片A4 打印优化基于 JDK 21
VERSION

版本与边界

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 会直接报错
LANGUAGE

语言表达力

3
record

数据类 + 校验

record Range(int lo, int hi) {
    Range {
        if (lo > hi) throw new IllegalArgumentException();
    }
}

紧凑构造器不写参数列表;字段自动 private final,并生成访问器、equals、hashCode、toString。

sealed

受控继承 + 穷尽 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 也能穷尽。

switch

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。

CONCURRENCY

并发模型

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 不继承。

COLLECTIONS

集合与流

3
Sequenced

首尾统一

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 / Stream

默认值与合并

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

核心 API

3
String / Math

JDK 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。

IO

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,仍建议显式指定。

HTTP

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。

MIGRATION

迁移与排障

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。

API

移除与禁用

  • Thread.stop/suspend/resume 抛 UnsupportedOperationException
  • Thread.countStackFrames 仍在,但同样抛 UOE;改用 getStackTrace / StackWalker
  • SecurityManager 21 默认禁用,24 永久移除(JEP 486)
  • 生产代码隔离预览特性:class 不能跨版本,升级时必须重编译

6 个最容易记错的点TRAPS

预览 ≠ 稳定

编译和运行都要 --enable-preview,class 的 minor 是 65535;换 JDK 必须重新编译,不能直接跑。

虚拟线程不是加速器

它提升吞吐与线程数量,不加速 CPU 密集任务;池化虚拟线程没有意义。JDK 21 的 synchronized 仍会 pinning。

ScopedValue 的继承边界

结构化子任务继承绑定;手工 new Thread 或 Thread.ofVirtual().start 不继承。21 的 runWhere 等旧 API 在 25 已移除。

FFM 的命名迁移

21 用 allocateUtf8String/getUtf8String,22+ 改为 allocateFrom/getString;21 仍是预览,需要 --enable-preview。

区间 indexOf 的坑

越界抛 StringIndexOutOfBoundsException,不是 -1;没有区间版 lastIndexOf。Math.clamp 在 min > max 时抛 IAE。

视图与拷贝

reversed() 是视图,原集合变化会反映;List.copyOf 是不可变拷贝,且拒绝 null。

继续深入NEXT

返回速查模块

内容基于本机 JDK 21.0.12 编译/运行验证;跨版本差异另用 JDK 17 / 24 / 25 核对。

JDK 学习手册 · JDK / Java 专用学习站 · 纯静态 · 双击 index.html 即可使用

本站专注 JDK 版本演进与 Java 教学,非 Oracle / OpenJDK 官方站点。版本数据来自 OpenJDK 官方 Features 表;课程示例已在对应本机 JDK 实测。构建日期 2026-11-12。