Skip to content

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 APIApplet 已废弃

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-8

JDK 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();  // 1

Foreign 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 17JDK 18JDK 19JDK 20JDK 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 为什么被称为里程碑版本?

  1. 虚拟线程正式发布:Java 并发编程的重大变革
  2. Record Patterns 正式发布:模式匹配能力增强
  3. Pattern Matching for switch 正式发布:switch 表达能力达到新高度
  4. Sequenced Collections:解决了集合元素顺序的标准化问题
  5. Project Amber 成果集中发布:近年来语法糖的集大成者

追问二:预览版和正式版有什么区别?

阶段说明
预览版未来可能会变,不保证稳定,不在生产环境使用
正式版语言特性稳定,可以放心使用

预览版的目的:

  1. 收集社区反馈
  2. 在实际项目中使用验证
  3. 允许不兼容变更

追问三: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 等因素。

基于 VitePress 构建