JDK 17 到 JDK 21 跨越了 JDK 18、19、20 三个非 LTS 版本。本文汇总这四个版本引入的重要变更,重点关注 breaking change 和需要主动迁移的部分。

JDK 18(2022 年 3 月)

Breaking Change

UTF-8 成为默认字符集(JEP 400)

这是 17 → 21 迁移中最常见的隐性 breaking change。JDK 18 起,Charset.defaultCharset() 固定返回 UTF-8,不再跟随操作系统区域设置。

受影响的场景:

  • new String(bytes) — 未指定编码,依赖平台默认值
  • new FileReader(file) — 未指定编码
  • System.out.println() 输出非 ASCII 字符
  • 读写文件时未显式指定编码

修复方式:所有涉及字节/字符转换的地方显式指定 StandardCharsets.UTF_8

// 修复前(行为依赖平台)
String s = new String(bytes);
new FileReader(file);

// 修复后(行为确定)
String s = new String(bytes, StandardCharsets.UTF_8);
new FileReader(file, StandardCharsets.UTF_8);

新特性

  • JEP 408: 简单 Web 服务器jwebserver 命令,用于本地静态文件服务,开发调试用
  • JEP 413: Java API 文档中的代码片段@snippet 标签)
  • JEP 416: 使用方法句柄重新实现反射核心(内部实现变更,API 不变)
  • JEP 417: 向量 API(第三次孵化)
  • JEP 418: 互联网地址解析 SPI — 可插拔的地址解析器,InetAddress 行为可自定义
  • JEP 419: 外部函数与内存 API(第二次孵化)
  • JEP 420: switch 模式匹配(第二次预览)
  • JEP 421: 废弃 Finalizationfinalize() 正式废弃,标记 forRemoval

JDK 19(2022 年 9 月)

重要预览/孵化特性

  • JEP 425: 虚拟线程(第一次预览) — Project Loom 的核心成果,轻量级线程
  • JEP 428: 结构化并发(孵化) — 将多个并发任务作为一个工作单元管理
  • JEP 405: Record 模式(第一次预览) — 在 instanceofswitch 中解构 record
  • JEP 427: switch 模式匹配(第三次预览)
  • JEP 424: 外部函数与内存 API(预览) — 从孵化升为预览
  • JEP 426: 向量 API(第四次孵化)

移除/废弃

  • Thread.stop(boolean) 已移除(Thread.stop() 无参版本在后续版本移除)

JDK 20(2023 年 3 月)

重要预览/孵化特性

  • JEP 436: 虚拟线程(第二次预览)
  • JEP 437: 结构化并发(第二次孵化)
  • JEP 432: Record 模式(第二次预览)
  • JEP 433: switch 模式匹配(第四次预览)
  • JEP 434: 外部函数与内存 API(第二次预览)
  • JEP 438: 向量 API(第五次孵化)
  • JEP 429: 作用域值(孵化) — 替代 ThreadLocal 的不可变线程绑定值

JDK 21(2023 年 9 月,LTS)

Breaking Change

Thread.stop() 等方法实际抛出异常

Thread.stop()Thread.suspend()Thread.resume() 在 JDK 21 调用时抛出 UnsupportedOperationException,不再有任何实际效果。

SecurityManager 不可用

JDK 17 废弃,JDK 21 调用 System.setSecurityManager() 会抛出 UnsupportedOperationException

正式特性(从预览/孵化毕业)

JEP 441: switch 模式匹配(正式版)

// 对任意类型进行模式匹配
Object obj = ...;
String result = switch (obj) {
    case Integer i -> "整数: " + i;
    case String s  -> "字符串: " + s;
    case null      -> "null";
    default        -> "其他";
};

JEP 440: Record 模式(正式版)

// 解构 record
if (obj instanceof Point(int x, int y)) {
    System.out.println(x + ", " + y);
}

// 嵌套解构
if (shape instanceof Rectangle(Point(int x, int y), int w, int h)) {
    // ...
}

JEP 444: 虚拟线程(正式版)

Project Loom 的核心特性正式毕业。虚拟线程是轻量级线程,由 JVM 调度而非操作系统,适合高并发 I/O 密集型场景。

// 创建虚拟线程
Thread vt = Thread.ofVirtual().start(() -> {
    System.out.println("虚拟线程运行中");
});

// 使用虚拟线程的 ExecutorService
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> doWork());
}

虚拟线程注意事项:

  • ThreadLocal 仍可用,但在虚拟线程上大量使用会有内存压力,建议改用 Scoped Values
  • 同步代码块(synchronized)会 pin 住载体线程,高并发时影响性能,建议改用 ReentrantLock
  • 虚拟线程不建议用线程池,每个任务直接创建新虚拟线程即可

JEP 445: 无名类和实例主方法(预览)

简化 Hello World,降低初学门槛(JDK 21 为预览版):

// 无需 class 声明,无需 public static void main
void main() {
    System.out.println("Hello, World!");
}

JEP 431: Sequenced Collections

新增三个接口,为有序集合提供统一的首尾操作 API:

  • SequencedCollectiongetFirst()getLast()addFirst()addLast()reversed()
  • SequencedSet
  • SequencedMapfirstEntry()lastEntry()reversed()
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
list.getFirst(); // "a"
list.getLast();  // "c"
list.reversed(); // ["c", "b", "a"]

JEP 430: 字符串模板(预览)

String name = "World";
String msg = STR."Hello, \{name}!";  // "Hello, World!"

JEP 442: 外部函数与内存 API(第三次预览)

JEP 446: 作用域值(预览)

替代 ThreadLocal 的方案,不可变、生命周期明确、对虚拟线程友好:

static final ScopedValue<String> USER = ScopedValue.newInstance();

ScopedValue.where(USER, "alice").run(() -> {
    System.out.println(USER.get()); // "alice"
});

JEP 453: 结构化并发(预览)

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Future<String> user  = scope.fork(() -> fetchUser());
    Future<Integer> order = scope.fork(() -> fetchOrder());
    scope.join().throwIfFailed();
    return new Response(user.resultNow(), order.resultNow());
}

LTS 版本支持周期

阶段 截止时间
Premier Support 2028 年 9 月
Extended Support 2031 年 9 月

变更汇总对照表

类别 变更项 引入版本 影响
Breaking 默认字符集改为 UTF-8 JDK 18 未指定编码的字节/字符转换可能乱码
Breaking Thread.stop() 抛异常 JDK 21 调用直接失败
Breaking SecurityManager 不可用 JDK 21 调用直接抛异常
Breaking finalize() 废弃 JDK 18(废弃) 将在后续版本移除
新特性 虚拟线程 JDK 21(正式) 高并发 I/O 场景性能大幅提升
新特性 switch 模式匹配 JDK 21(正式) 替代繁琐的 instanceof 链
新特性 Record 模式 JDK 21(正式) 解构 record,配合 switch 使用
新特性 Sequenced Collections JDK 21(正式) 有序集合统一 API
新特性 结构化并发 JDK 21(预览) 简化并发任务管理
新特性 作用域值 JDK 21(预览) 替代 ThreadLocal

参考