JDK 17~21 新特性
JDK 17 是一个重要的 LTS 版本,而 JDK 21 更是一个里程碑式的 LTS。
如果你还在用 JDK 8,甚至 JDK 11,是时候看看 Java 这几年都发生了什么变化。
今天,我们来全面了解 JDK 17~21 的新特性。
版本时间线
JDK 17 │ 2021年9月 │ LTS
JDK 18 │ 2022年3月 │ 非 LTS
JDK 19 │ 2022年9月 │ 非 LTS
JDK 20 │ 2023年3月 │ 非 LTS
JDK 21 │ 2023年9月 │ LTS ← 里程碑版本JDK 17 新特性
Sealed Classes(正式版)
密封类在 JDK 17 成为正式特性:
java
public sealed class Shape permits Circle, Rectangle, Square {
// 只有 Circle、Rectangle、Square 可以继承
}
// 子类必须是 final、sealed 或 non-sealed
public final class Circle extends Shape { }
public sealed class Rectangle extends Shape permits ColoredRectangle { }
public non-sealed class Square extends Shape { }详细讲解请阅读 Sealed Class 密封类
Pattern Matching for switch(预览)
java
// JDK 17 预览
String describe = switch (obj) {
case Integer i -> "Integer: " + i;
case String s -> "String: " + s;
case null, default -> "Other";
};其他新特性
| 特性 | 说明 |
|---|---|
| 恢复始终严格的浮点语义 | StrictMath 始终严格 |
| 增强的伪随机数生成器 | 新增 RandomGenerator 接口 |
| 新的 macOS 渲染管道 | 使用 Apple Metal API |
| 移除 Applet API | Applet 已废弃 |
JDK 18~20 新特性
预览特性一览
JDK 18 │ UTF-8 默认、Simple Web Server、jwebserver
JDK 19 │ Virtual Threads(预览3)、Foreign Function & Memory API(预览2)
JDK 20 │ Virtual Threads(预览4)、Record Patterns(预览2)JDK 18:UTF-8 成为默认字符集
java
// JDK 18 之前:依赖系统默认编码
// JDK 18+:默认 UTF-8,更可预测
Reader reader = new FileReader("file.txt"); // UTF-8JDK 19:Virtual Threads 第一次预览
java
// JDK 19 预览 API
Thread virtualThread = Thread.ofVirtual()
.name("my-vt")
.start(() -> System.out.println("Running!"));JDK 21 新特性(重点)
JDK 21 是继 JDK 17 之后的又一个 LTS,被 Oracle 称为「The Most Feature-LTS Release」。
虚拟线程(正式版)
java
// JDK 21 正式版
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
} // 自动释放详细讲解请阅读 Virtual Thread 虚拟线程
Record Patterns(正式版)
java
// JDK 21 正式版
Object obj = new Point(3, 4);
// 类型模式 + Record Pattern 组合
if (obj instanceof Point(int x, int y)) {
System.out.println("Distance from origin: " + Math.sqrt(x*x + y*y));
}
// switch 中的 Record Pattern
String describe = switch (obj) {
case Point(int x, int y) when x == y -> "On diagonal";
case Point(int x, int y) -> "Not on diagonal";
default -> "Not a point";
};详细讲解请阅读 Record 类型与模式匹配
Pattern Matching for switch(正式版)
java
// JDK 21 正式版
String format(Object obj) {
return switch (obj) {
case null -> "null";
case Integer i -> String.format("int %d", i);
case String s && s.length() > 10 -> "long string";
case String s -> "string: " + s;
case int[] arr -> "int array of length " + arr.length;
default -> "unknown";
};
}详细讲解请阅读 Switch 表达式增强
Sequenced Collections(正式版)
定义集合中元素的顺序:
java
// 新增接口
interface SequencedCollection<E> extends Collection<E> {
E getFirst();
E getLast();
void addFirst(E);
void addLast(E);
}
// List 和 Deque 已实现
List<String> list = new ArrayList<>();
list.addFirst("first");
list.addLast("last");
String first = list.getFirst(); // "first"
// LinkedHashSet 和 LinkedHashMap 也实现了
Set<Integer> set = new LinkedHashSet<>();
set.add(1);
set.add(2);
int first = set.getFirst(); // 1Foreign Function & Memory API(正式版)
在 Java 中调用 native 代码,无需 JNI:
java
// JDK 21 正式版
MemorySegment segment = MemorySegment.ofArray(new byte[1024]);
VarHandle byteHandle = MemoryHandles.varHandle(byte.class, ByteOrder.LITTLE_ENDIAN);
// 写入
byteHandle.set(segment, 0L, (byte) 42);
// 读取
byte b = (byte) byteHandle.get(segment, 0L);String Templates(预览)
java
// JDK 21 预览
String name = "Java";
String greeting = STR."Hello, \{name}!"; // "Hello, Java!"
// 嵌入式表达式
int x = 10, y = 20;
String result = STR."\{x} + \{y} = \{x + y}"; // "10 + 20 = 30"
// RAW 模板处理器
String raw = RAW."raw \{name} \n"; // 保留 \n 的原始含义Unnamed Patterns and Variables(正式版)
java
// JDK 21 正式版
// 不使用的变量用 _ 表示
switch (obj) {
case String _ -> "It's a string";
case Integer _ -> "It's an integer";
default -> "Unknown";
}
// catch 块中忽略异常
try {
// ...
} catch (Exception _) { // 不使用异常对象
logger.error("Failed");
}
// lambda 参数忽略
list.forEach(_ -> System.out.println("side effect"));Unnamed Classes and Instance Main Methods(正式版)
简化单文件程序的 main 方法:
java
// JDK 21
// 无需 class 声明
void main() {
System.out.println("Hello!");
}新特性总结表
| 特性 | JDK 17 | JDK 18 | JDK 19 | JDK 20 | JDK 21 |
|---|---|---|---|---|---|
| Sealed Classes | 正式版 | - | - | - | - |
| Virtual Threads | - | - | 预览3 | 预览4 | 正式版 |
| Record Patterns | 预览 | 预览2 | 预览2 | 预览2 | 正式版 |
| Pattern Matching switch | 预览 | 预览2 | 预览3 | 预览4 | 正式版 |
| Sequenced Collections | - | - | - | - | 正式版 |
| Foreign Function & Memory | 预览 | 预览2 | 预览2 | 预览3 | 正式版 |
| String Templates | - | - | 预览1 | 预览2 | 预览3 |
| Unnamed Patterns | - | - | - | 预览4 | 正式版 |
面试追问方向
追问一:JDK 21 为什么被称为里程碑版本?
- 虚拟线程正式发布:Java 并发编程的重大变革
- Record Patterns 正式发布:模式匹配能力增强
- Pattern Matching for switch 正式发布:switch 表达能力达到新高度
- Sequenced Collections:解决了集合元素顺序的标准化问题
- Project Amber 成果集中发布:近年来语法糖的集大成者
追问二:预览版和正式版有什么区别?
| 阶段 | 说明 |
|---|---|
| 预览版 | 未来可能会变,不保证稳定,不在生产环境使用 |
| 正式版 | 语言特性稳定,可以放心使用 |
预览版的目的:
- 收集社区反馈
- 在实际项目中使用验证
- 允许不兼容变更
追问三:Virtual Threads 和线程池有什么关系?
传统线程池:
java
ExecutorService executor = Executors.newFixedThreadPool(100);
executor.submit(() -> {
// 阻塞 IO 操作
callExternalService();
});虚拟线程:
java
// JDK 21
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
// 不再需要池化虚拟线程关键区别:
- 虚拟线程不需要池化,因为创建成本极低
- 阻塞虚拟线程不会阻塞 carrier thread
- 一个 carrier thread 可以承载多个虚拟线程
留给你的思考题
我们讲了 JDK 17~21 的新特性,Virtual Threads 绝对是其中的明星。
但有一个问题:
虚拟线程这么好,是不是意味着我们可以把所有的 Thread 都换成 Virtual Thread?
什么时候用虚拟线程,什么时候用平台线程?
提示:考虑 CPU 密集型 vs IO 密集型、线程本地存储(ThreadLocal)、synchronized 等因素。
