很多公司使用的 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 获取与升级建议
- OpenJDK 21 官方下载
- Oracle JDK 21 下载
- 生产环境建议选择 LTS 版本,如 JDK 17、JDK 21。
- 升级需关注依赖库兼容性、API 变更等。
2. JDK 历史版本
Java 自 1995 年发布以来,经历了多次重要版本的演进。下面梳理 Java 主要版本迭代及其代表性特性。
- Java 9 起,Java 改为半年发布一次新版本(每年3月和9月),提高了创新速度。
- LTS(长期支持)版本一般为每三年一次(如 8、11、17、21),适合生产使用。
2.1. 早期版本(JDK 1.x)
| 版本 | 发布年份 | 主要特性 |
|---|---|---|
| JDK 1.0 | 1996 | Java 初版,基础语法、AWT |
| JDK 1.1 | 1997 | 内部类、反射、JDBC |
2.2. 经典版本(J2SE 1.x)
| 版本 | 发布年份 | 主要特性 |
|---|---|---|
| J2SE 1.2 | 1998 | 集合框架(Collections)、Swing |
| J2SE 1.3 | 2000 | 热启动、JPDA |
| J2SE 1.4 | 2002 | assert、NIO、正则表达式、日志 |
2.3. 现代Java(Java SE 5~8)
| 版本 | 发布年份 | 主要特性 |
|---|---|---|
| Java 5 | 2004 | 泛型、注解、枚举、自动装箱 |
| Java 6 | 2006 | 脚本引擎、JDBC 4.0 |
| Java 7 | 2011 | switch 支持字符串、try-with-resources、NIO.2 |
| Java 8 | 2014 | Lambda、Stream、默认接口方法、Optional |
2.4. 新时代Java(Java 9~当前)
| 版本 | 发布年份 | 主要特性 |
|---|---|---|
| Java 9 | 2017 | 模块系统(JPMS)、JShell |
| Java 10 | 2018 | 局部变量类型推断(var) |
| Java 11 | 2018 | LTS 版本、HTTP Client |
| Java 12 | 2019 | Switch 表达式(预览) |
| Java 13 | 2019 | 文本块(Text Blocks,预览) |
| Java 14 | 2020 | Record(预览)、NullPointerException增强 |
| Java 15 | 2020 | Sealed Classes(预览)、隐藏类 |
| Java 16 | 2021 | Record(正式)、Pattern Matching for instanceof |
| Java 17 | 2021 | LTS 版本、Sealed Classes(正式)、强封装 |
| Java 18 | 2022 | 简化 UTF-8 默认、简单 Web 服务器 |
| Java 19 | 2022 | 虚拟线程(预览)、结构化并发(预览) |
| Java 20 | 2023 | 虚拟线程(预览)、记录模式(预览) |
| Java 21 | 2023 | LTS 版本、虚拟线程(正式)、模式匹配 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 8 | 2014年3月 | 长期支持(已过主流支持,部分厂商仍支持) |
| JDK 11 | 2018年9月 | 长期支持 |
| JDK 17 | 2021年9月 | 长期支持 |
| JDK 21 | 2023年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):
takeWhile、dropWhile、ofNullable等增强(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)
发展历史与版本
设计背景
简化多行字符串书写,减少转义,提高可读性,便于表达 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)
发展历史与版本
设计背景
限制类的继承,提高类型安全,方便模式匹配和未来的模式解构。
功能范围
- 通过
sealed修饰符声明,指定允许继承的类(permits)。 - 子类必须为
final、sealed或non-sealed。
示例代码
JDK 8:无继承限制
abstract class Shape {}
class Circle extends Shape {}
class Rectangle extends Shape {}
// 任何类都可扩展 ShapeJDK 17+(JDK 21):密封类
public sealed abstract class Shape permits Circle, Rectangle {}
public final class Circle extends Shape {}
public final class Rectangle extends Shape {}
// 只有列出的类可以继承 Shape6. 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 结合,可直接解构数据对象,提升代码简洁性与安全性。
功能范围
- 支持在
instanceof、switch等场景下直接解构 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)
发展历史与版本
设计背景
将并发任务的生命周期限定在作用域内,提升可维护性和异常安全性,防止资源泄漏。
功能范围
- 使用
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 表达式、Record、Sealed 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 历史版本在开发上的一些特性提升。
官方文档和参考:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用@来通知其他用户。