很多公司使用的 JDK 还停留在 JDK8,JDK8、JDK11、JDK17、JDK21 都是 LTS 版本。不想升级 JDK11、JDK17 的人,大多是觉得没有特别值得更新的新特性。但就算每个版本都是小迭代,经历了这么多版本了,JDK21 相较于 JDK8,也是有不少值得升级的闪光点。

1. JDK 21 简介

JDK 21(Java Development Kit 21)于 2023年9月19日 正式发布,是 Java 的一个长期支持版本(LTS,Long-Term Support)。作为 LTS 版本,JDK 21 将获得多年的官方维护和安全更新,因此被广泛推荐用于生产环境。

主要新特性

JDK 21 带来了许多创新和改进,部分重点包括:

  • 虚拟线程(Virtual Threads):极大简化高并发编程(JEP 444,正式特性)。
  • 模式匹配 for switch:提升 switch 表达式的灵活性和安全性(JEP 441)。
  • 记录模式(Record Patterns):解构数据对象更便捷(JEP 440)。
  • 序列集合(Sequenced Collections):引入新集合接口,统一有序集合(JEP 431)。
  • 结构化并发(Structured Concurrency):改进并发任务管理(JEP 453,预览)。
  • 字符串模板(String Templates):更安全、灵活的字符串插值(JEP 430,预览)。
  • 垃圾回收优化:ZGC、G1、Shenandoah 等进一步优化。
  • 安全性与性能提升:多项底层优化。

JDK 21 继续巩固 Java 现代化、易用性和高性能的方向,是新一代 Java 应用的首选平台。

JDK 21 获取与升级建议

2. JDK 历史版本

Java 自 1995 年发布以来,经历了多次重要版本的演进。下面梳理 Java 主要版本迭代及其代表性特性。

  • Java 9 起,Java 改为半年发布一次新版本(每年3月和9月),提高了创新速度。
  • LTS(长期支持)版本一般为每三年一次(如 8、11、17、21),适合生产使用。

2.1. 早期版本(JDK 1.x)

版本发布年份主要特性
JDK 1.01996Java 初版,基础语法、AWT
JDK 1.11997内部类、反射、JDBC

2.2. 经典版本(J2SE 1.x)

版本发布年份主要特性
J2SE 1.21998集合框架(Collections)、Swing
J2SE 1.32000热启动、JPDA
J2SE 1.42002assert、NIO、正则表达式、日志

2.3. 现代Java(Java SE 5~8)

版本发布年份主要特性
Java 52004泛型、注解、枚举、自动装箱
Java 62006脚本引擎、JDBC 4.0
Java 72011switch 支持字符串、try-with-resources、NIO.2
Java 82014Lambda、Stream、默认接口方法、Optional

2.4. 新时代Java(Java 9~当前)

版本发布年份主要特性
Java 92017模块系统(JPMS)、JShell
Java 102018局部变量类型推断(var)
Java 112018LTS 版本、HTTP Client
Java 122019Switch 表达式(预览)
Java 132019文本块(Text Blocks,预览)
Java 142020Record(预览)、NullPointerException增强
Java 152020Sealed Classes(预览)、隐藏类
Java 162021Record(正式)、Pattern Matching for instanceof
Java 172021LTS 版本、Sealed Classes(正式)、强封装
Java 182022简化 UTF-8 默认、简单 Web 服务器
Java 192022虚拟线程(预览)、结构化并发(预览)
Java 202023虚拟线程(预览)、记录模式(预览)
Java 212023LTS 版本、虚拟线程(正式)、模式匹配 for switch、序列集合等

2.5. LTS 版本

JDK LTS 版本指的是 Java Development Kit(JDK)长期支持(Long-Term Support, LTS)版本

意味着该版本会获得较长时间的官方技术支持、bug修复和安全更新,通常为至少3年甚至更长

为什么需要LTS版本?

Java每6个月发布一个新版本,但大多数版本的支持周期很短(仅6个月)。而LTS版本会获得更长时间的更新和维护,非常适合企业级和生产环境使用。非LTS版本主要用于尝鲜新特性。

目前的JDK LTS版本

截至2024年6月,主要的JDK LTS版本包括:

JDK版本发布时间支持周期
JDK 82014年3月长期支持(已过主流支持,部分厂商仍支持)
JDK 112018年9月长期支持
JDK 172021年9月长期支持
JDK 212023年9月长期支持

其中,JDK 8、11、17、21都是LTS版本,JDK 9、10、12、13、14、15、16、18、19、20 等则是非LTS版本。

选择建议
  • 生产环境、企业应用:建议选择最新的LTS版本(如JDK 17或JDK 21)。
  • 学习、尝试新特性:可以选择非LTS版本,但需注意生命周期较短。

3. 流(Stream)API 增强

发展历史与版本
  • JDK 8(2014):首次引入 Stream API(JEP 107)。
  • JDK 9(2017)takeWhiledropWhileofNullable 等增强(JEP 269)。
  • JDK 16(2021)toList() 方法正式加入。
  • JDK 21(2023):引入 mapMulti,提升多对多映射效率(JEP 430)。
设计背景

传统 flatMap 需要为每个元素返回一个 Stream,存在性能开销。mapMulti 允许直接对每个元素调用 Consumer,多对多处理更加高效。

功能范围
  • 支持用 mapMulti 替代部分 flatMap 场景,简化代码、提升性能。
示例代码

JDK 8:使用 flatMap

List<Integer> numbers = Arrays.asList(1, 2, 3);
List<Integer> result = numbers.stream()
    .flatMap(n -> Stream.of(n, n * 10))
    .collect(Collectors.toList());
// 输出: [1, 10, 2, 20, 3, 30]

JDK 21:使用 mapMulti(JDK 21 新增)

List<Integer> numbers = Arrays.asList(1, 2, 3);
List<Integer> result = numbers.stream()
    .<Integer>mapMulti((n, consumer) -> {
        consumer.accept(n);
        consumer.accept(n * 10);
    })
    .collect(Collectors.toList());
// 输出: [1, 10, 2, 20, 3, 30]

区别mapMulti 避免了频繁创建临时 Stream,代码更高效。

4. 文本块(Text Blocks)

发展历史与版本
  • JDK 8 及之前:无多行字符串,需手动拼接。
  • JDK 13(2019,预览):引入文本块(JEP 355)。
  • JDK 15(2020,正式):文本块成为正式特性(JEP 378)。
设计背景

简化多行字符串书写,减少转义,提高可读性,便于表达 JSON、SQL、HTML 等。

功能范围
  • 使用 """ 包裹多行字符串,自动处理缩进和换行。
示例代码

JDK 8:多行字符串

String json = "{\n" +
              "  \"name\": \"Alice\",\n" +
              "  \"age\": 25\n" +
              "}";

JDK 15+(JDK 21):文本块

String json = """
              {
                "name": "Alice",
                "age": 25
              }
              """;

5. 密封类(Sealed Classes)

发展历史与版本
  • JDK 15(2020,预览):密封类(JEP 360)。
  • JDK 17(2021,正式):密封类正式加入(JEP 409)。
设计背景

限制类的继承,提高类型安全,方便模式匹配和未来的模式解构。

功能范围
  • 通过 sealed 修饰符声明,指定允许继承的类(permits)。
  • 子类必须为 finalsealednon-sealed
示例代码

JDK 8:无继承限制

abstract class Shape {}
class Circle extends Shape {}
class Rectangle extends Shape {}
// 任何类都可扩展 Shape

JDK 17+(JDK 21):密封类

public sealed abstract class Shape permits Circle, Rectangle {}
public final class Circle extends Shape {}
public final class Rectangle extends Shape {}
// 只有列出的类可以继承 Shape

6. switch 提升

发展历史与版本
  • JDK 8 及之前:仅支持整数、字符串、枚举,语法有限。
  • JDK 12(2019,预览):引入 switch 表达式(JEP 325)。
  • JDK 14(2020,正式):switch 表达式正式特性(JEP 361)。
  • JDK 17(2021,预览):switch 模式匹配(JEP 406)。
  • JDK 21(2023,正式):switch 模式匹配正式特性(JEP 441)。
设计背景
  • 赋值能力(表达式)。
  • 多标签 case。
  • 模式匹配,简化类型判断。
  • 箭头语法,自动 break,减少冗余。
功能范围
  • switch 既可做语句也可做表达式,直接赋值。
  • case 可支持多个标签。
  • case 可匹配类型、解构对象。
  • 支持箭头语法,无需 break。
示例1. switch 表达式(可赋值,JDK 12+)

JDK 8:只能做语句,不能赋值

String day = "MONDAY";
int num;
switch (day) {
    case "MONDAY": num = 1; break;
    case "TUESDAY": num = 2; break;
    default: num = 0;
}

JDK 21:switch 表达式

String day = "MONDAY";
int num = switch (day) {
    case "MONDAY" -> 1;
    case "TUESDAY" -> 2;
    default -> 0;
};
示例2. 多标签 case(JDK 12+)
String day = "SATURDAY";
String type = switch (day) {
    case "SATURDAY", "SUNDAY" -> "Weekend";
    default -> "Weekday";
};
示例3. 箭头语法(JDK 12+)
int score = 85;
String grade = switch (score / 10) {
    case 10, 9 -> "A";
    case 8 -> "B";
    case 7 -> "C";
    default -> "D";
};
示例4. 模式匹配(Pattern Matching for switch, JDK 21)
Object obj = 123;
String result = switch (obj) {
    case String s -> "字符串长度: " + s.length();
    case Integer i -> "整数值: " + i;
    default -> "未知类型";
};
示例5. 解构 Record(结合 Record Patterns, JDK 21)
record Point(int x, int y) {}
Object obj = new Point(1, 2);
String str = switch (obj) {
    case Point(int x, int y) -> "Point: " + x + "," + y;
    default -> "其他类型";
};

7. Record模式(Record Patterns)

发展历史与版本
  • JDK 14(2020,预览):Record 类型(JEP 359)。
  • JDK 16(2021,正式):Record 类型正式特性(JEP 395)。
  • JDK 19(2022,预览):Record Patterns(JEP 405)。
  • JDK 21(2023,正式):Record Patterns 正式特性(JEP 440)。
设计背景

模式匹配与 Record 结合,可直接解构数据对象,提升代码简洁性与安全性。

功能范围
  • 支持在 instanceofswitch 等场景下直接解构 Record 成员。
示例代码

JDK 8:手动提取

public class Point {
    private final int x, y;
    public Point(int x, int y) { this.x = x; this.y = y; }
    public int getX() { return x; }
    public int getY() { return y; }
}
Point p = new Point(1, 2);
if (p != null) {
    int x = p.getX();
    int y = p.getY();
    System.out.println(x + ", " + y);
}

JDK 21:Record Patterns

record Point(int x, int y) {}
Object obj = new Point(1, 2);
if (obj instanceof Point(int x, int y)) {
    System.out.println(x + ", " + y);
}

// switch 结合 record patterns
String s = switch (obj) {
    case Point(int x, int y) -> "x=" + x + ", y=" + y;
    default -> "unknown";
};

8. 字符串模板(String Templates)

发展历史与版本
  • JDK 21(2023,预览):字符串模板引入(JEP 430)。
设计背景

提高字符串拼接、格式化、内嵌表达式的安全性与易用性,便于生成 SQL、JSON 等。

功能范围
  • 使用 STR."..." 语法,\{} 内可嵌入变量或表达式。
  • 可扩展用于 SQL/JSON 等安全模板。
示例代码

JDK 8:字符串拼接或 format

String name = "Alice";
int age = 25;
String msg = "Name: " + name + ", Age: " + age;
String msg2 = String.format("Name: %s, Age: %d", name, age);

JDK 21:字符串模板

String name = "Alice";
int age = 25;
String msg = STR."Name: \{name}, Age: \{age}";
// 输出: Name: Alice, Age: 25

// 还可以做表达式
String msg2 = STR."Next year: \{age + 1}";

9. 未命名变量和未命名模式(Unnamed Patterns and Variables)

发展历史与版本
  • JDK 21(2023,预览):引入未命名变量与未命名模式(JEP 443)。
设计背景

模式匹配时,忽略不关心的变量,提升代码可读性。

功能范围
  • 使用 _ 表示未命名变量或模式。
示例代码

JDK 8:必须定义所有变量

record Point(int x, int y) {}
Point p = new Point(1, 2);
int x = p.x(); // 如果只关心 x,y 仍需定义

JDK 21:未命名变量与模式

record Point(int x, int y) {}
Object obj = new Point(1, 2);
if (obj instanceof Point(int x, _)) {
    System.out.println("x = " + x);
}

// switch 也支持
String s = switch (obj) {
    case Point(int x, _) -> "x=" + x;
    default -> "unknown";
};

10. 结构化并发(Structured Concurrency)

发展历史与版本
  • JDK 19(2022,预览):结构化并发(JEP 428)。
  • JDK 21(2023,预览):结构化并发增强(JEP 453)。
设计背景

将并发任务的生命周期限定在作用域内,提升可维护性和异常安全性,防止资源泄漏。

功能范围
  • 使用 StructuredTaskScope 管理子任务,自动取消失败任务。
  • 子任务结果聚合、异常传播更安全。
示例代码

JDK 8:手动管理线程池

ExecutorService executor = Executors.newFixedThreadPool(2);
Future<String> f1 = executor.submit(() -> "foo");
Future<String> f2 = executor.submit(() -> "bar");
String result = f1.get() + f2.get();
executor.shutdown();

JDK 21:结构化并发

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Future<String> f1 = scope.fork(() -> "foo");
    Future<String> f2 = scope.fork(() -> "bar");
    scope.join();              // 等待所有任务完成
    scope.throwIfFailed();     // 任何子任务失败则抛异常
    String result = f1.resultNow() + f2.resultNow();
    System.out.println(result);
}

11. 尾言

11.1. "预览" 和 "正式"

一句话总结

预览功能需要手动开启、允许变动,不建议生产用;正式功能是稳定定稿、直接可用,推荐生产用。

详细解释

  • 预览功能是“试验田”,目的是让社区提前体验新特性,发现问题、收集反馈,之后决定是否、如何成为正式标准。
  • 正式功能是“定稿版”,经过充分验证和社区讨论后,成为 Java 语言和平台的标准组成部分,可放心用于生产。

对比总结

项目预览(Preview)正式(Standard/Final)
默认可用性否,需要 --enable-preview是,直接可用
稳定性不稳定,可能变动稳定,承诺向后兼容
生产环境不推荐推荐
反馈目的是,鼓励开发者反馈已定稿,无需反馈
示例JDK19 虚拟线程、switch表达式Lambda、Record、JDK21虚拟线程

“预览”功能(Preview Feature)

预览功能,是 JDK 新增特性的一个发布阶段。它允许开发者提前试用和反馈新特性,但该特性尚未正式成为 Java 标准的一部分。

主要特征
  • 默认关闭:预览功能在 JDK 中默认关闭,需通过编译和运行参数手动启用(如 --enable-preview)。
  • 不保证兼容:预览功能的语法、API 可能在后续 JDK 版本中被修改、增强甚至移除。
  • 鼓励反馈:Oracle 和社区希望开发者在实际项目中尝试预览特性,反馈问题与建议。
  • 不会进入 LTS 长期支持:只有“正式”特性才会纳入 LTS(如 JDK 17/21),预览特性不会。
启用方法
  • 编译时:javac --enable-preview --release 21 YourClass.java
  • 运行时:java --enable-preview YourClass
常见例子
  • 虚拟线程(JEP 425/436):在 JDK 19/20 中为预览功能。
  • switch 表达式RecordSealed Class:曾以预览功能发布,后转为正式特性。

“正式”功能(Standard/Final Feature)

正式功能,是指经过 JCP(Java Community Process)标准化流程,最终确定并纳入 Java 语言和 API 标准的特性。

主要特征
  • 默认开启:无需任何参数,直接可用。
  • 向后兼容:API 和语法稳定,后续版本不会随意变更或移除。
  • 纳入 LTS 支持:正式特性会被长期支持(如 JDK 17、21)。
  • 可用于生产环境:推荐生产使用。
常见例子
  • Lambda 表达式(JDK 8)
  • Stream API(JDK 8)
  • Record(JDK 16 正式)
  • 虚拟线程(JDK 21 正式)

11.2. 尾言

JDK 21 的新特性中有最关键的两个没有介绍:

  • 虚拟线程(Virtual Threads)
  • ZGC 垃圾收集器

篇幅原因,这两个后面分别单独介绍。本章主要介绍,JDK 历史版本在开发上的一些特性提升。

官方文档和参考:

KerryWu
679 声望171 粉丝

保持饥饿


引用和评论

0 条评论