--- title: Java 24 新特性概览 description: 总结 JDK 24 的新特性与改动,便于跟踪 Java 演进。 category: Java tag: - Java新特性 head: - - meta - name: keywords content: Java 24,JDK24,JEP 更新,语言特性,GC 改进,平台增强 --- JDK 24 于 2025 年 3 月发布,这是一个非 LTS(长期支持版)版本。它的下一个版本是 2025 年 9 月发布的 LTS 版本 **JDK 25**。 JDK 24 共有 24 个新特性,这篇文章会挑选其中较为重要的一些新特性进行详细介绍: - [JEP 478: Key Derivation Function API (密钥派生函数 API, 预览)](https://openjdk.org/jeps/478) - [JEP 483: Early Class-File Loading & Linking (提前类加载和链接)](https://openjdk.org/jeps/483) - [JEP 484: Class File API (类文件 API)](https://openjdk.org/jeps/484) - [JEP 485: Stream Gatherers (流收集器)](https://openjdk.org/jeps/485) - [JEP 486: Disable the Security Manager (永久禁用安全管理器)](https://openjdk.org/jeps/486) - [JEP 487: Scoped Values (作用域值, 第四次预览)](https://openjdk.org/jeps/487) - [JEP 495: Simplified Source Files and Instance Main Methods (简化的源文件和实例主方法, 第四次预览)](https://openjdk.org/jeps/495) - [JEP 497: Quantum-Resistant Digital Signature Algorithm (ML-DSA) (量子抗性数字签名算法)](https://openjdk.org/jeps/497) - [JEP 499: Structured Concurrency (结构化并发, 第四次预览)](https://openjdk.org/jeps/499) 下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间: ![](https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png) ## JEP 478: Key Derivation Function API(密钥派生函数 API,预览) 密钥派生函数 API 提供了一套用于从初始密钥和其他数据派生额外密钥的标准接口。它可以为不同的加密目的(如加密、认证等)生成多个不同的密钥,避免直接重复使用同一密钥。该 API 在 JDK 24 中处于预览阶段。 通过该 API,开发者可以使用 HKDF 等由安全提供者实现的密钥派生算法: ```java // 创建一个 KDF 对象,使用 HKDF-SHA256 算法 KDF hkdf = KDF.getInstance("HKDF-SHA256"); // 创建 Extract 和 Expand 参数规范 AlgorithmParameterSpec params = HKDFParameterSpec.ofExtract() .addIKM(initialKeyMaterial) // 设置初始密钥材料 .addSalt(salt) // 设置盐值 .thenExpand(info, 32); // 设置扩展信息和目标长度 // 派生一个 32 字节的 AES 密钥 SecretKey key = hkdf.deriveKey("AES", params); // 可以使用相同的 KDF 对象进行其他密钥派生操作 ``` ## JEP 483: Early Class-File Loading & Linking(提前类加载和链接) 在传统 JVM 中,应用在每次启动时需要动态加载和链接类。该特性通过缓存已加载和链接的类,减少后续启动中的重复工作。JEP 483 给出的 Spring PetClinic 基准中,使用 AOT 缓存后启动时间最多缩短了约 42%;实际收益取决于应用和运行环境。 这个优化不要求修改应用程序、库或框架代码,但需要先记录训练运行并创建 AOT 缓存,随后在正式启动时加载该缓存。相关参数包括 `-XX:AOTMode=record`、`-XX:AOTConfiguration`、`-XX:AOTMode=create` 和 `-XX:AOTCache`。 ## JEP 484: Class File API(类文件 API) 类文件 API 在 JDK 22 进行了第一次预览([JEP 457](https://openjdk.org/jeps/457)),在 JDK 23 进行了第二次预览并进一步完善([JEP 466](https://openjdk.org/jeps/466))。最终,该特性在 JDK 24 中顺利转正。 类文件 API 的目标是提供一套标准化的 API,用于解析、生成和转换 Java 类文件,取代过去对第三方库(如 ASM)在类文件处理上的依赖。 ```java // 创建一个 ClassFile 对象,这是操作类文件的入口。 ClassFile cf = ClassFile.of(); // 解析字节数组为 ClassModel ClassModel classModel = cf.parse(bytes); // 构建新的类文件,移除以 "debug" 开头的所有方法 byte[] newBytes = cf.build(classModel.thisClass().asSymbol(), classBuilder -> { // 遍历所有类元素 for (ClassElement ce : classModel) { // 判断是否为方法 且 方法名以 "debug" 开头 if (!(ce instanceof MethodModel mm && mm.methodName().stringValue().startsWith("debug"))) { // 添加到新的类文件中 classBuilder.with(ce); } } }); ``` ## JEP 485: Stream Gatherers(流收集器) 流收集器 `Stream::gather(Gatherer)` 是一个强大的新特性,它允许开发者定义自定义的中间操作,从而实现更复杂、更灵活的数据转换。`Gatherer` 接口是该特性的核心,它定义了如何从流中收集元素,维护中间状态,并在处理过程中生成结果。 与现有的 `filter`、`map` 或 `distinct` 等内置操作不同,`Stream::gather` 使得开发者能够实现那些难以用标准 Stream 操作完成的任务。例如,可以使用 `Stream::gather` 实现滑动窗口、自定义规则的去重、或者更复杂的状态转换和聚合。 这种灵活性极大地扩展了 Stream API 的应用范围,使开发者能够应对更复杂的数据处理场景。 基于 `Stream::gather(Gatherer)` 实现字符串长度的去重逻辑: ```java var result = Stream.of("foo", "bar", "baz", "quux") .gather(Gatherer.ofSequential( HashSet::new, // 初始化状态为 HashSet,用于保存已经遇到过的字符串长度 (set, str, downstream) -> { if (set.add(str.length())) { return downstream.push(str); } return true; // 继续处理流 } )) .toList();// 转换为列表 // 输出结果 ==> [foo, quux] ``` ## JEP 486: Disable the Security Manager(永久禁用安全管理器) JDK 24 不再允许启用 `Security Manager`,即使通过 `java -Djava.security.manager` 命令也无法启用,这是逐步移除该功能的关键一步。虽然 `Security Manager` 曾经是 Java 中限制代码权限(如访问文件系统或网络、读取或写入敏感文件、执行系统命令)的重要工具,但由于复杂性高、使用率低且维护成本大,Java 社区决定最终移除它。 ## JEP 487: Scoped Values(作用域值, 第四次预览) 作用域值(Scoped Values)可以在线程内和线程间共享不可变的数据,优于线程局部变量,尤其是在使用大量虚拟线程时。 ```java final static ScopedValue<...> V = ScopedValue.newInstance(); // In some method ScopedValue.where(V, ) .run(() -> { ... V.get() ... call methods ... }); // In a method called directly or indirectly from the lambda expression ... V.get() ... ``` 作用域值允许在大型程序中的组件之间安全有效地共享数据,而无需求助于方法参数。 ## JEP 491: Virtual Threads Synchronization Without Pinning(虚拟线程的同步而不固定平台线程) 优化了虚拟线程与 `synchronized` 的工作机制。 虚拟线程在 `synchronized` 方法和代码块中阻塞时,通常能够释放其占用的操作系统线程(平台线程),避免了对平台线程的长时间占用,从而提升应用程序的并发能力。 这种机制避免了“固定 (Pinning)”——即虚拟线程长时间占用平台线程,阻止其服务于其他虚拟线程的情况。 现有的使用 `synchronized` 的 Java 代码无需修改即可受益于虚拟线程的扩展能力。 例如,一个 I/O 密集型的应用程序,如果使用传统的平台线程,可能会因为线程阻塞而导致并发能力下降。 而使用虚拟线程,即使在 `synchronized` 块中发生阻塞,也不会固定平台线程,从而允许平台线程继续服务于其他虚拟线程,提高整体的并发性能。 ## JEP 493: Linking Run-Time Images Without JMOD Files(在没有 JMOD 文件的情况下链接运行时镜像) 默认情况下,JDK 同时包含运行时镜像(运行时所需的模块)和 JMOD 文件。这个特性使得 jlink 工具无需使用 JDK 的 JMOD 文件就可以创建自定义运行时镜像,减少了 JDK 的安装体积(约 25%)。 说明: - Jlink 是随 Java 9 一起发布的新命令行工具。它允许开发人员为基于模块的 Java 应用程序创建自己的轻量级、定制的 JRE。 - JMOD 文件是模块化归档文件,可以包含类文件、原生库、配置文件、头文件、法律声明等内容。 ## JEP 495: Simplified Source Files and Instance Main Methods(简化的源文件和实例主方法, 第四次预览) 这个特性主要简化了 `main` 方法的声明。对于 Java 初学者来说,这个 `main` 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。 没有使用该特性之前定义一个 `main` 方法: ```java public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } ``` 使用该新特性之后定义一个 `main` 方法: ```java class HelloWorld { void main() { System.out.println("Hello, World!"); } } ``` 进一步简化(未命名的类允许我们省略类名) ```java void main() { System.out.println("Hello, World!"); } ``` ## JEP 497: Quantum-Resistant Digital Signature Algorithm (ML-DSA)(量子抗性数字签名算法) JDK 24 引入了支持实施抗量子的基于模块晶格的数字签名算法(Module-Lattice-Based Digital Signature Algorithm, **ML-DSA**),为抵御未来量子计算机可能带来的威胁做准备。 ML-DSA 是美国国家标准与技术研究院(NIST)在 FIPS 204 中标准化的量子抗性算法,用于数字签名和身份验证。 ## JEP 498: Warnings When Using `sun.misc.Unsafe` Memory Access Methods (使用 `sun.misc.Unsafe` 内存访问方法时发出警告) JDK 23([JEP 471](https://openjdk.org/jeps/471)) 提议弃用 `sun.misc.Unsafe` 中的内存访问方法,这些方法将来的版本中会被移除。在 JDK 24 中,当首次调用 `sun.misc.Unsafe` 的任何内存访问方法时,运行时会发出警告。 这些不安全的方法已有安全高效的替代方案: - `java.lang.invoke.VarHandle`:JDK 9 (JEP 193) 中引入,提供了一种安全有效地操作堆内存的方法,包括对象的字段、类的静态字段以及数组元素。 - `java.lang.foreign.MemorySegment`:JDK 22 (JEP 454) 中引入,提供了一种安全有效地访问堆外内存的方法,有时会与 `VarHandle` 协同工作。 `MemorySegment` 是 Foreign Function & Memory API(外部函数和内存 API)的核心类型之一,用于安全访问堆内或堆外内存;`VarHandle` 是独立的标准 API,可用于访问字段、数组元素或按内存布局访问数据。Foreign Function & Memory API 在 JDK 22 中转为正式特性。 ```java import java.lang.foreign.*; import java.lang.invoke.VarHandle; // 管理堆外整数数组的类 class OffHeapIntBuffer { // 用于访问整数元素的VarHandle private static final VarHandle ELEM_VH = ValueLayout.JAVA_INT.arrayElementVarHandle(); // 内存管理器 private final Arena arena; // 堆外内存段 private final MemorySegment buffer; // 构造函数,分配指定数量的整数空间 public OffHeapIntBuffer(long size) { this.arena = Arena.ofShared(); this.buffer = arena.allocate(ValueLayout.JAVA_INT, size); } // 释放内存 public void deallocate() { arena.close(); } // 以volatile方式设置指定索引的值 public void setVolatile(long index, int value) { ELEM_VH.setVolatile(buffer, 0L, index, value); } // 初始化指定范围的元素为0 public void initialize(long start, long n) { buffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start, ValueLayout.JAVA_INT.byteSize() * n) .fill((byte) 0); } // 将指定范围的元素复制到新数组 public int[] copyToNewArray(long start, int n) { return buffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start, ValueLayout.JAVA_INT.byteSize() * n) .toArray(ValueLayout.JAVA_INT); } } ``` ## JEP 499: Structured Concurrency(结构化并发, 第四次预览) JDK 19 以孵化器 API 的形式引入了结构化并发。JDK 24 中该 API 处于第四次预览阶段,目的是简化多线程编程,并不是为了取代 `java.util.concurrent`。 结构化并发将不同线程中运行的多个任务视为单个工作单元,从而简化错误处理、提高可靠性并增强可观察性。也就是说,结构化并发保留了单线程代码的可读性、可维护性和可观察性。 结构化并发的基本 API 是 `StructuredTaskScope`,它支持将任务拆分为多个并发子任务,在它们自己的线程中执行,并且子任务必须在主任务继续之前完成。 `StructuredTaskScope` 的基本用法如下: ```java try (var scope = new StructuredTaskScope()) { // 使用fork方法派生线程来执行子任务 Subtask subtask1 = scope.fork(task1); Subtask subtask2 = scope.fork(task2); // 等待线程完成 scope.join(); // 结果的处理可能包括处理或重新抛出异常 ... process results/exceptions ... } // close ``` 结构化并发非常适合虚拟线程,虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程,从而允许非常多的虚拟线程。