Latest commit

History

History
266 lines (192 loc) · 13.6 KB

File metadata and controls

266 lines (192 loc) · 13.6 KB
titleJava 24 新特性概览
description总结 JDK 24 的新特性与改动,便于跟踪 Java 演进。
categoryJava
tag
Java新特性
head
meta
namecontent
keywords
Java 24,JDK24,JEP 更新,语言特性,GC 改进,平台增强

JDK 24 于 2025 年 3 月发布,这是一个非 LTS(长期支持版)版本。下一个长期支持版是 JDK 25,预计于 2025 年 9 月发布。

JDK 24 共有 24 个新特性,这篇文章会挑选其中较为重要的一些新特性进行详细介绍:

下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间:

JEP 478: Key Derivation Function API (密钥派生函数 API)

密钥派生函数 API 是一种用于从初始密钥和其他数据派生额外密钥的加密算法。它的核心作用是为不同的加密目的(如加密、认证等)生成多个不同的密钥,避免密钥重复使用带来的安全隐患。这在新代加密中是一个重要的里程碑,为后续新兴的量子计算环境打下了基础。

通过该 API,开发者可以使用最新的密钥派生算法(如 HKDF 和未来的 Argon2):

// 创建一个 KDF 对象,使用 HKDF-SHA256 算法KDFhkdf = KDF.getInstance("HKDF-SHA256");
// 创建 Extract 和 Expand 参数规范AlgorithmParameterSpecparams =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial) // 设置初始密钥材料
.addSalt(salt) // 设置盐值
.thenExpand(info, 32); // 设置扩展信息和目标长度// 派生一个 32 字节的 AES 密钥SecretKeykey = hkdf.deriveKey("AES", params);
// 可以使用相同的 KDF 对象进行其他密钥派生操作

JEP 483: Early Class-File Loading & Linking (提前类加载和链接)

在传统 JVM 中,应用在每次启动时需要动态加载和链接类。这种机制对启动时间敏感的应用(如微服务或无服务器函数)带来了显著的性能瓶颈。该特性通过缓存已加载和链接的类,显著减少了重复工作的开销,显著减少 Java 应用程序的启动时间。测试表明,对大型应用(如基于 Spring 的服务器应用),启动时间可减少 40% 以上。

这个优化是零侵入性的,对应用程序、库或框架的代码无需任何更改,启动也方式保持一致,仅需添加相关 JVM 参数(如 -XX:+ClassDataSharing)。

JEP 484: Class File API (类文件 API)

类文件 API 在 JDK 22 进行了第一次预览(JEP 457),在 JDK 23 进行了第二次预览并进一步完善(JEP 466)。最终,该特性在 JDK 24 中顺利转正。

类文件 API 的目标是提供一套标准化的 API,用于解析、生成和转换 Java 类文件,取代过去对第三方库(如 ASM)在类文件处理上的依赖。

// 创建一个 ClassFile 对象,这是操作类文件的入口。ClassFilecf = ClassFile.of();
// 解析字节数组为 ClassModelClassModelclassModel = cf.parse(bytes);
// 构建新的类文件,移除以 "debug" 开头的所有方法byte[] newBytes = cf.build(classModel.thisClass().asSymbol(),
classBuilder -> {
// 遍历所有类元素for (ClassElementce : classModel) {
// 判断是否为方法 且 方法名以 "debug" 开头if (!(ceinstanceofMethodModelmm
&& mm.methodName().stringValue().startsWith("debug"))) {
// 添加到新的类文件中classBuilder.with(ce);
}
}
});

JEP 485: Stream Gatherers (流收集器)

流收集器 Stream::gather(Gatherer) 是一个强大的新特性,它允许开发者定义自定义的中间操作,从而实现更复杂、更灵活的数据转换。Gatherer 接口是该特性的核心,它定义了如何从流中收集元素,维护中间状态,并在处理过程中生成结果。

与现有的 filtermapdistinct 等内置操作不同,Stream::gather 使得开发者能够实现那些难以用标准 Stream 操作完成的任务。例如,可以使用 Stream::gather 实现滑动窗口、自定义规则的去重、或者更复杂的状态转换和聚合。 这种灵活性极大地扩展了 Stream API 的应用范围,使开发者能够应对更复杂的数据处理场景。

基于 Stream::gather(Gatherer) 实现字符串长度的去重逻辑:

varresult = Stream.of("foo", "bar", "baz", "quux")
.gather(Gatherer.ofSequential(
HashSet::new, // 初始化状态为 HashSet,用于保存已经遇到过的字符串长度
(set, str, downstream) -> {
if (set.add(str.length())) {
returndownstream.push(str);
}
returntrue; // 继续处理流
}
))
.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)可以在线程内和线程间共享不可变的数据,优于线程局部变量,尤其是在使用大量虚拟线程时。

finalstaticScopedValue<...> V = newScopedValue<>();
// In some methodScopedValue.where(V, <value>)
.run(() -> { ... V.get() ... callmethods ... });
// 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 文件是 Java 模块的描述文件,包含了模块的元数据和资源。

JEP 495: Simplified Source Files and Instance Main Methods (简化的源文件和实例主方法, 第四次预览)

这个特性主要简化了 main 方法的声明。对于 Java 初学者来说,这个 main 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。

没有使用该特性之前定义一个 main 方法:

publicclassHelloWorld {
publicstaticvoidmain(String[] args) {
System.out.println("Hello, World!");
}
}

使用该新特性之后定义一个 main 方法:

classHelloWorld {
voidmain() {
System.out.println("Hello, World!");
}
}

进一步简化(未命名的类允许我们省略类名)

voidmain() {
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) 提议弃用 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 协同工作。

这两个类是 Foreign Function & Memory API(外部函数和内存 API) 的核心组件,分别用于管理和操作堆外内存。Foreign Function & Memory API 在 JDK 22 中正式转正,成为标准特性。

importjdk.incubator.foreign.*;
importjava.lang.invoke.VarHandle;
// 管理堆外整数数组的类classOffHeapIntBuffer {
// 用于访问整数元素的VarHandleprivatestaticfinalVarHandleELEM_VH = ValueLayout.JAVA_INT.arrayElementVarHandle();
// 内存管理器privatefinalArenaarena;
// 堆外内存段privatefinalMemorySegmentbuffer;
// 构造函数,分配指定数量的整数空间publicOffHeapIntBuffer(longsize) {
this.arena = Arena.ofShared();
this.buffer = arena.allocate(ValueLayout.JAVA_INT, size);
}
// 释放内存publicvoiddeallocate() {
arena.close();
}
// 以volatile方式设置指定索引的值publicvoidsetVolatile(longindex, intvalue) {
ELEM_VH.setVolatile(buffer, 0L, index, value);
}
// 初始化指定范围的元素为0publicvoidinitialize(longstart, longn) {
buffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.fill((byte) 0);
}
// 将指定范围的元素复制到新数组publicint[] copyToNewArray(longstart, intn) {
returnbuffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.toArray(ValueLayout.JAVA_INT);
}
}

JEP 499: Structured Concurrency (结构化并发, 第四次预览)

JDK 19 引入了结构化并发,一种多线程编程方法,目的是为了通过结构化并发 API 来简化多线程编程,并不是为了取代java.util.concurrent,目前处于孵化器阶段。

结构化并发将不同线程中运行的多个任务视为单个工作单元,从而简化错误处理、提高可靠性并增强可观察性。也就是说,结构化并发保留了单线程代码的可读性、可维护性和可观察性。

结构化并发的基本 API 是StructuredTaskScope,它支持将任务拆分为多个并发子任务,在它们自己的线程中执行,并且子任务必须在主任务继续之前完成。

StructuredTaskScope 的基本用法如下:

try (varscope = newStructuredTaskScope<Object>()) {
// 使用fork方法派生线程来执行子任务Future<Integer> future1 = scope.fork(task1);
Future<String> future2 = scope.fork(task2);
// 等待线程完成scope.join();
// 结果的处理可能包括处理或重新抛出异常
... processresults/exceptions ...
} // close

结构化并发非常适合虚拟线程,虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程,从而允许非常多的虚拟线程。

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

History
266 lines (192 loc) · 13.6 KB

File metadata and controls

266 lines (192 loc) · 13.6 KB
titleJava 24 新特性概览
description总结 JDK 24 的新特性与改动,便于跟踪 Java 演进。
categoryJava
tag
Java新特性
head
meta
namecontent
keywords
Java 24,JDK24,JEP 更新,语言特性,GC 改进,平台增强

JDK 24 于 2025 年 3 月发布,这是一个非 LTS(长期支持版)版本。下一个长期支持版是 JDK 25,预计于 2025 年 9 月发布。

JDK 24 共有 24 个新特性,这篇文章会挑选其中较为重要的一些新特性进行详细介绍:

下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间:

JEP 478: Key Derivation Function API (密钥派生函数 API)

密钥派生函数 API 是一种用于从初始密钥和其他数据派生额外密钥的加密算法。它的核心作用是为不同的加密目的(如加密、认证等)生成多个不同的密钥,避免密钥重复使用带来的安全隐患。这在新代加密中是一个重要的里程碑,为后续新兴的量子计算环境打下了基础。

通过该 API,开发者可以使用最新的密钥派生算法(如 HKDF 和未来的 Argon2):

// 创建一个 KDF 对象,使用 HKDF-SHA256 算法KDFhkdf = KDF.getInstance("HKDF-SHA256");
// 创建 Extract 和 Expand 参数规范AlgorithmParameterSpecparams =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial) // 设置初始密钥材料
.addSalt(salt) // 设置盐值
.thenExpand(info, 32); // 设置扩展信息和目标长度// 派生一个 32 字节的 AES 密钥SecretKeykey = hkdf.deriveKey("AES", params);
// 可以使用相同的 KDF 对象进行其他密钥派生操作

JEP 483: Early Class-File Loading & Linking (提前类加载和链接)

在传统 JVM 中,应用在每次启动时需要动态加载和链接类。这种机制对启动时间敏感的应用(如微服务或无服务器函数)带来了显著的性能瓶颈。该特性通过缓存已加载和链接的类,显著减少了重复工作的开销,显著减少 Java 应用程序的启动时间。测试表明,对大型应用(如基于 Spring 的服务器应用),启动时间可减少 40% 以上。

这个优化是零侵入性的,对应用程序、库或框架的代码无需任何更改,启动也方式保持一致,仅需添加相关 JVM 参数(如 -XX:+ClassDataSharing)。

JEP 484: Class File API (类文件 API)

类文件 API 在 JDK 22 进行了第一次预览(JEP 457),在 JDK 23 进行了第二次预览并进一步完善(JEP 466)。最终,该特性在 JDK 24 中顺利转正。

类文件 API 的目标是提供一套标准化的 API,用于解析、生成和转换 Java 类文件,取代过去对第三方库(如 ASM)在类文件处理上的依赖。

// 创建一个 ClassFile 对象,这是操作类文件的入口。ClassFilecf = ClassFile.of();
// 解析字节数组为 ClassModelClassModelclassModel = cf.parse(bytes);
// 构建新的类文件,移除以 "debug" 开头的所有方法byte[] newBytes = cf.build(classModel.thisClass().asSymbol(),
classBuilder -> {
// 遍历所有类元素for (ClassElementce : classModel) {
// 判断是否为方法 且 方法名以 "debug" 开头if (!(ceinstanceofMethodModelmm
&& mm.methodName().stringValue().startsWith("debug"))) {
// 添加到新的类文件中classBuilder.with(ce);
}
}
});

JEP 485: Stream Gatherers (流收集器)

流收集器 Stream::gather(Gatherer) 是一个强大的新特性,它允许开发者定义自定义的中间操作,从而实现更复杂、更灵活的数据转换。Gatherer 接口是该特性的核心,它定义了如何从流中收集元素,维护中间状态,并在处理过程中生成结果。

与现有的 filtermapdistinct 等内置操作不同,Stream::gather 使得开发者能够实现那些难以用标准 Stream 操作完成的任务。例如,可以使用 Stream::gather 实现滑动窗口、自定义规则的去重、或者更复杂的状态转换和聚合。 这种灵活性极大地扩展了 Stream API 的应用范围,使开发者能够应对更复杂的数据处理场景。

基于 Stream::gather(Gatherer) 实现字符串长度的去重逻辑:

varresult = Stream.of("foo", "bar", "baz", "quux")
.gather(Gatherer.ofSequential(
HashSet::new, // 初始化状态为 HashSet,用于保存已经遇到过的字符串长度
(set, str, downstream) -> {
if (set.add(str.length())) {
returndownstream.push(str);
}
returntrue; // 继续处理流
}
))
.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)可以在线程内和线程间共享不可变的数据,优于线程局部变量,尤其是在使用大量虚拟线程时。

finalstaticScopedValue<...> V = newScopedValue<>();
// In some methodScopedValue.where(V, <value>)
.run(() -> { ... V.get() ... callmethods ... });
// 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 文件是 Java 模块的描述文件,包含了模块的元数据和资源。

JEP 495: Simplified Source Files and Instance Main Methods (简化的源文件和实例主方法, 第四次预览)

这个特性主要简化了 main 方法的声明。对于 Java 初学者来说,这个 main 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。

没有使用该特性之前定义一个 main 方法:

publicclassHelloWorld {
publicstaticvoidmain(String[] args) {
System.out.println("Hello, World!");
}
}

使用该新特性之后定义一个 main 方法:

classHelloWorld {
voidmain() {
System.out.println("Hello, World!");
}
}

进一步简化(未命名的类允许我们省略类名)

voidmain() {
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) 提议弃用 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 协同工作。

这两个类是 Foreign Function & Memory API(外部函数和内存 API) 的核心组件,分别用于管理和操作堆外内存。Foreign Function & Memory API 在 JDK 22 中正式转正,成为标准特性。

importjdk.incubator.foreign.*;
importjava.lang.invoke.VarHandle;
// 管理堆外整数数组的类classOffHeapIntBuffer {
// 用于访问整数元素的VarHandleprivatestaticfinalVarHandleELEM_VH = ValueLayout.JAVA_INT.arrayElementVarHandle();
// 内存管理器privatefinalArenaarena;
// 堆外内存段privatefinalMemorySegmentbuffer;
// 构造函数,分配指定数量的整数空间publicOffHeapIntBuffer(longsize) {
this.arena = Arena.ofShared();
this.buffer = arena.allocate(ValueLayout.JAVA_INT, size);
}
// 释放内存publicvoiddeallocate() {
arena.close();
}
// 以volatile方式设置指定索引的值publicvoidsetVolatile(longindex, intvalue) {
ELEM_VH.setVolatile(buffer, 0L, index, value);
}
// 初始化指定范围的元素为0publicvoidinitialize(longstart, longn) {
buffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.fill((byte) 0);
}
// 将指定范围的元素复制到新数组publicint[] copyToNewArray(longstart, intn) {
returnbuffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.toArray(ValueLayout.JAVA_INT);
}
}

JEP 499: Structured Concurrency (结构化并发, 第四次预览)

JDK 19 引入了结构化并发,一种多线程编程方法,目的是为了通过结构化并发 API 来简化多线程编程,并不是为了取代java.util.concurrent,目前处于孵化器阶段。

结构化并发将不同线程中运行的多个任务视为单个工作单元,从而简化错误处理、提高可靠性并增强可观察性。也就是说,结构化并发保留了单线程代码的可读性、可维护性和可观察性。

结构化并发的基本 API 是StructuredTaskScope,它支持将任务拆分为多个并发子任务,在它们自己的线程中执行,并且子任务必须在主任务继续之前完成。

StructuredTaskScope 的基本用法如下:

try (varscope = newStructuredTaskScope<Object>()) {
// 使用fork方法派生线程来执行子任务Future<Integer> future1 = scope.fork(task1);
Future<String> future2 = scope.fork(task2);
// 等待线程完成scope.join();
// 结果的处理可能包括处理或重新抛出异常
... processresults/exceptions ...
} // close

结构化并发非常适合虚拟线程,虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程,从而允许非常多的虚拟线程。

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

History
266 lines (192 loc) · 13.6 KB

File metadata and controls

266 lines (192 loc) · 13.6 KB
titleJava 24 新特性概览
description总结 JDK 24 的新特性与改动,便于跟踪 Java 演进。
categoryJava
tag
Java新特性
head
meta
namecontent
keywords
Java 24,JDK24,JEP 更新,语言特性,GC 改进,平台增强

JDK 24 于 2025 年 3 月发布,这是一个非 LTS(长期支持版)版本。下一个长期支持版是 JDK 25,预计于 2025 年 9 月发布。

JDK 24 共有 24 个新特性,这篇文章会挑选其中较为重要的一些新特性进行详细介绍:

下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间:

JEP 478: Key Derivation Function API (密钥派生函数 API)

密钥派生函数 API 是一种用于从初始密钥和其他数据派生额外密钥的加密算法。它的核心作用是为不同的加密目的(如加密、认证等)生成多个不同的密钥,避免密钥重复使用带来的安全隐患。这在新代加密中是一个重要的里程碑,为后续新兴的量子计算环境打下了基础。

通过该 API,开发者可以使用最新的密钥派生算法(如 HKDF 和未来的 Argon2):

// 创建一个 KDF 对象,使用 HKDF-SHA256 算法KDFhkdf = KDF.getInstance("HKDF-SHA256");
// 创建 Extract 和 Expand 参数规范AlgorithmParameterSpecparams =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial) // 设置初始密钥材料
.addSalt(salt) // 设置盐值
.thenExpand(info, 32); // 设置扩展信息和目标长度// 派生一个 32 字节的 AES 密钥SecretKeykey = hkdf.deriveKey("AES", params);
// 可以使用相同的 KDF 对象进行其他密钥派生操作

JEP 483: Early Class-File Loading & Linking (提前类加载和链接)

在传统 JVM 中,应用在每次启动时需要动态加载和链接类。这种机制对启动时间敏感的应用(如微服务或无服务器函数)带来了显著的性能瓶颈。该特性通过缓存已加载和链接的类,显著减少了重复工作的开销,显著减少 Java 应用程序的启动时间。测试表明,对大型应用(如基于 Spring 的服务器应用),启动时间可减少 40% 以上。

这个优化是零侵入性的,对应用程序、库或框架的代码无需任何更改,启动也方式保持一致,仅需添加相关 JVM 参数(如 -XX:+ClassDataSharing)。

JEP 484: Class File API (类文件 API)

类文件 API 在 JDK 22 进行了第一次预览(JEP 457),在 JDK 23 进行了第二次预览并进一步完善(JEP 466)。最终,该特性在 JDK 24 中顺利转正。

类文件 API 的目标是提供一套标准化的 API,用于解析、生成和转换 Java 类文件,取代过去对第三方库(如 ASM)在类文件处理上的依赖。

// 创建一个 ClassFile 对象,这是操作类文件的入口。ClassFilecf = ClassFile.of();
// 解析字节数组为 ClassModelClassModelclassModel = cf.parse(bytes);
// 构建新的类文件,移除以 "debug" 开头的所有方法byte[] newBytes = cf.build(classModel.thisClass().asSymbol(),
classBuilder -> {
// 遍历所有类元素for (ClassElementce : classModel) {
// 判断是否为方法 且 方法名以 "debug" 开头if (!(ceinstanceofMethodModelmm
&& mm.methodName().stringValue().startsWith("debug"))) {
// 添加到新的类文件中classBuilder.with(ce);
}
}
});

JEP 485: Stream Gatherers (流收集器)

流收集器 Stream::gather(Gatherer) 是一个强大的新特性,它允许开发者定义自定义的中间操作,从而实现更复杂、更灵活的数据转换。Gatherer 接口是该特性的核心,它定义了如何从流中收集元素,维护中间状态,并在处理过程中生成结果。

与现有的 filtermapdistinct 等内置操作不同,Stream::gather 使得开发者能够实现那些难以用标准 Stream 操作完成的任务。例如,可以使用 Stream::gather 实现滑动窗口、自定义规则的去重、或者更复杂的状态转换和聚合。 这种灵活性极大地扩展了 Stream API 的应用范围,使开发者能够应对更复杂的数据处理场景。

基于 Stream::gather(Gatherer) 实现字符串长度的去重逻辑:

varresult = Stream.of("foo", "bar", "baz", "quux")
.gather(Gatherer.ofSequential(
HashSet::new, // 初始化状态为 HashSet,用于保存已经遇到过的字符串长度
(set, str, downstream) -> {
if (set.add(str.length())) {
returndownstream.push(str);
}
returntrue; // 继续处理流
}
))
.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)可以在线程内和线程间共享不可变的数据,优于线程局部变量,尤其是在使用大量虚拟线程时。

finalstaticScopedValue<...> V = newScopedValue<>();
// In some methodScopedValue.where(V, <value>)
.run(() -> { ... V.get() ... callmethods ... });
// 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 文件是 Java 模块的描述文件,包含了模块的元数据和资源。

JEP 495: Simplified Source Files and Instance Main Methods (简化的源文件和实例主方法, 第四次预览)

这个特性主要简化了 main 方法的声明。对于 Java 初学者来说,这个 main 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。

没有使用该特性之前定义一个 main 方法:

publicclassHelloWorld {
publicstaticvoidmain(String[] args) {
System.out.println("Hello, World!");
}
}

使用该新特性之后定义一个 main 方法:

classHelloWorld {
voidmain() {
System.out.println("Hello, World!");
}
}

进一步简化(未命名的类允许我们省略类名)

voidmain() {
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) 提议弃用 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 协同工作。

这两个类是 Foreign Function & Memory API(外部函数和内存 API) 的核心组件,分别用于管理和操作堆外内存。Foreign Function & Memory API 在 JDK 22 中正式转正,成为标准特性。

importjdk.incubator.foreign.*;
importjava.lang.invoke.VarHandle;
// 管理堆外整数数组的类classOffHeapIntBuffer {
// 用于访问整数元素的VarHandleprivatestaticfinalVarHandleELEM_VH = ValueLayout.JAVA_INT.arrayElementVarHandle();
// 内存管理器privatefinalArenaarena;
// 堆外内存段privatefinalMemorySegmentbuffer;
// 构造函数,分配指定数量的整数空间publicOffHeapIntBuffer(longsize) {
this.arena = Arena.ofShared();
this.buffer = arena.allocate(ValueLayout.JAVA_INT, size);
}
// 释放内存publicvoiddeallocate() {
arena.close();
}
// 以volatile方式设置指定索引的值publicvoidsetVolatile(longindex, intvalue) {
ELEM_VH.setVolatile(buffer, 0L, index, value);
}
// 初始化指定范围的元素为0publicvoidinitialize(longstart, longn) {
buffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.fill((byte) 0);
}
// 将指定范围的元素复制到新数组publicint[] copyToNewArray(longstart, intn) {
returnbuffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.toArray(ValueLayout.JAVA_INT);
}
}

JEP 499: Structured Concurrency (结构化并发, 第四次预览)

JDK 19 引入了结构化并发,一种多线程编程方法,目的是为了通过结构化并发 API 来简化多线程编程,并不是为了取代java.util.concurrent,目前处于孵化器阶段。

结构化并发将不同线程中运行的多个任务视为单个工作单元,从而简化错误处理、提高可靠性并增强可观察性。也就是说,结构化并发保留了单线程代码的可读性、可维护性和可观察性。

结构化并发的基本 API 是StructuredTaskScope,它支持将任务拆分为多个并发子任务,在它们自己的线程中执行,并且子任务必须在主任务继续之前完成。

StructuredTaskScope 的基本用法如下:

try (varscope = newStructuredTaskScope<Object>()) {
// 使用fork方法派生线程来执行子任务Future<Integer> future1 = scope.fork(task1);
Future<String> future2 = scope.fork(task2);
// 等待线程完成scope.join();
// 结果的处理可能包括处理或重新抛出异常
... processresults/exceptions ...
} // close

结构化并发非常适合虚拟线程,虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程,从而允许非常多的虚拟线程。

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

History
266 lines (192 loc) · 13.6 KB

File metadata and controls

266 lines (192 loc) · 13.6 KB
titleJava 24 新特性概览
description总结 JDK 24 的新特性与改动,便于跟踪 Java 演进。
categoryJava
tag
Java新特性
head
meta
namecontent
keywords
Java 24,JDK24,JEP 更新,语言特性,GC 改进,平台增强

JDK 24 于 2025 年 3 月发布,这是一个非 LTS(长期支持版)版本。下一个长期支持版是 JDK 25,预计于 2025 年 9 月发布。

JDK 24 共有 24 个新特性,这篇文章会挑选其中较为重要的一些新特性进行详细介绍:

下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间:

JEP 478: Key Derivation Function API (密钥派生函数 API)

密钥派生函数 API 是一种用于从初始密钥和其他数据派生额外密钥的加密算法。它的核心作用是为不同的加密目的(如加密、认证等)生成多个不同的密钥,避免密钥重复使用带来的安全隐患。这在新代加密中是一个重要的里程碑,为后续新兴的量子计算环境打下了基础。

通过该 API,开发者可以使用最新的密钥派生算法(如 HKDF 和未来的 Argon2):

// 创建一个 KDF 对象,使用 HKDF-SHA256 算法KDFhkdf = KDF.getInstance("HKDF-SHA256");
// 创建 Extract 和 Expand 参数规范AlgorithmParameterSpecparams =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial) // 设置初始密钥材料
.addSalt(salt) // 设置盐值
.thenExpand(info, 32); // 设置扩展信息和目标长度// 派生一个 32 字节的 AES 密钥SecretKeykey = hkdf.deriveKey("AES", params);
// 可以使用相同的 KDF 对象进行其他密钥派生操作

JEP 483: Early Class-File Loading & Linking (提前类加载和链接)

在传统 JVM 中,应用在每次启动时需要动态加载和链接类。这种机制对启动时间敏感的应用(如微服务或无服务器函数)带来了显著的性能瓶颈。该特性通过缓存已加载和链接的类,显著减少了重复工作的开销,显著减少 Java 应用程序的启动时间。测试表明,对大型应用(如基于 Spring 的服务器应用),启动时间可减少 40% 以上。

这个优化是零侵入性的,对应用程序、库或框架的代码无需任何更改,启动也方式保持一致,仅需添加相关 JVM 参数(如 -XX:+ClassDataSharing)。

JEP 484: Class File API (类文件 API)

类文件 API 在 JDK 22 进行了第一次预览(JEP 457),在 JDK 23 进行了第二次预览并进一步完善(JEP 466)。最终,该特性在 JDK 24 中顺利转正。

类文件 API 的目标是提供一套标准化的 API,用于解析、生成和转换 Java 类文件,取代过去对第三方库(如 ASM)在类文件处理上的依赖。

// 创建一个 ClassFile 对象,这是操作类文件的入口。ClassFilecf = ClassFile.of();
// 解析字节数组为 ClassModelClassModelclassModel = cf.parse(bytes);
// 构建新的类文件,移除以 "debug" 开头的所有方法byte[] newBytes = cf.build(classModel.thisClass().asSymbol(),
classBuilder -> {
// 遍历所有类元素for (ClassElementce : classModel) {
// 判断是否为方法 且 方法名以 "debug" 开头if (!(ceinstanceofMethodModelmm
&& mm.methodName().stringValue().startsWith("debug"))) {
// 添加到新的类文件中classBuilder.with(ce);
}
}
});

JEP 485: Stream Gatherers (流收集器)

流收集器 Stream::gather(Gatherer) 是一个强大的新特性,它允许开发者定义自定义的中间操作,从而实现更复杂、更灵活的数据转换。Gatherer 接口是该特性的核心,它定义了如何从流中收集元素,维护中间状态,并在处理过程中生成结果。

与现有的 filtermapdistinct 等内置操作不同,Stream::gather 使得开发者能够实现那些难以用标准 Stream 操作完成的任务。例如,可以使用 Stream::gather 实现滑动窗口、自定义规则的去重、或者更复杂的状态转换和聚合。 这种灵活性极大地扩展了 Stream API 的应用范围,使开发者能够应对更复杂的数据处理场景。

基于 Stream::gather(Gatherer) 实现字符串长度的去重逻辑:

varresult = Stream.of("foo", "bar", "baz", "quux")
.gather(Gatherer.ofSequential(
HashSet::new, // 初始化状态为 HashSet,用于保存已经遇到过的字符串长度
(set, str, downstream) -> {
if (set.add(str.length())) {
returndownstream.push(str);
}
returntrue; // 继续处理流
}
))
.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)可以在线程内和线程间共享不可变的数据,优于线程局部变量,尤其是在使用大量虚拟线程时。

finalstaticScopedValue<...> V = newScopedValue<>();
// In some methodScopedValue.where(V, <value>)
.run(() -> { ... V.get() ... callmethods ... });
// 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 文件是 Java 模块的描述文件,包含了模块的元数据和资源。

JEP 495: Simplified Source Files and Instance Main Methods (简化的源文件和实例主方法, 第四次预览)

这个特性主要简化了 main 方法的声明。对于 Java 初学者来说,这个 main 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。

没有使用该特性之前定义一个 main 方法:

publicclassHelloWorld {
publicstaticvoidmain(String[] args) {
System.out.println("Hello, World!");
}
}

使用该新特性之后定义一个 main 方法:

classHelloWorld {
voidmain() {
System.out.println("Hello, World!");
}
}

进一步简化(未命名的类允许我们省略类名)

voidmain() {
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) 提议弃用 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 协同工作。

这两个类是 Foreign Function & Memory API(外部函数和内存 API) 的核心组件,分别用于管理和操作堆外内存。Foreign Function & Memory API 在 JDK 22 中正式转正,成为标准特性。

importjdk.incubator.foreign.*;
importjava.lang.invoke.VarHandle;
// 管理堆外整数数组的类classOffHeapIntBuffer {
// 用于访问整数元素的VarHandleprivatestaticfinalVarHandleELEM_VH = ValueLayout.JAVA_INT.arrayElementVarHandle();
// 内存管理器privatefinalArenaarena;
// 堆外内存段privatefinalMemorySegmentbuffer;
// 构造函数,分配指定数量的整数空间publicOffHeapIntBuffer(longsize) {
this.arena = Arena.ofShared();
this.buffer = arena.allocate(ValueLayout.JAVA_INT, size);
}
// 释放内存publicvoiddeallocate() {
arena.close();
}
// 以volatile方式设置指定索引的值publicvoidsetVolatile(longindex, intvalue) {
ELEM_VH.setVolatile(buffer, 0L, index, value);
}
// 初始化指定范围的元素为0publicvoidinitialize(longstart, longn) {
buffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.fill((byte) 0);
}
// 将指定范围的元素复制到新数组publicint[] copyToNewArray(longstart, intn) {
returnbuffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.toArray(ValueLayout.JAVA_INT);
}
}

JEP 499: Structured Concurrency (结构化并发, 第四次预览)

JDK 19 引入了结构化并发,一种多线程编程方法,目的是为了通过结构化并发 API 来简化多线程编程,并不是为了取代java.util.concurrent,目前处于孵化器阶段。

结构化并发将不同线程中运行的多个任务视为单个工作单元,从而简化错误处理、提高可靠性并增强可观察性。也就是说,结构化并发保留了单线程代码的可读性、可维护性和可观察性。

结构化并发的基本 API 是StructuredTaskScope,它支持将任务拆分为多个并发子任务,在它们自己的线程中执行,并且子任务必须在主任务继续之前完成。

StructuredTaskScope 的基本用法如下:

try (varscope = newStructuredTaskScope<Object>()) {
// 使用fork方法派生线程来执行子任务Future<Integer> future1 = scope.fork(task1);
Future<String> future2 = scope.fork(task2);
// 等待线程完成scope.join();
// 结果的处理可能包括处理或重新抛出异常
... processresults/exceptions ...
} // close

结构化并发非常适合虚拟线程,虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程,从而允许非常多的虚拟线程。

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

History
266 lines (192 loc) · 13.6 KB

File metadata and controls

266 lines (192 loc) · 13.6 KB
titleJava 24 新特性概览
description总结 JDK 24 的新特性与改动,便于跟踪 Java 演进。
categoryJava
tag
Java新特性
head
meta
namecontent
keywords
Java 24,JDK24,JEP 更新,语言特性,GC 改进,平台增强

JDK 24 于 2025 年 3 月发布,这是一个非 LTS(长期支持版)版本。下一个长期支持版是 JDK 25,预计于 2025 年 9 月发布。

JDK 24 共有 24 个新特性,这篇文章会挑选其中较为重要的一些新特性进行详细介绍:

下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间:

JEP 478: Key Derivation Function API (密钥派生函数 API)

密钥派生函数 API 是一种用于从初始密钥和其他数据派生额外密钥的加密算法。它的核心作用是为不同的加密目的(如加密、认证等)生成多个不同的密钥,避免密钥重复使用带来的安全隐患。这在新代加密中是一个重要的里程碑,为后续新兴的量子计算环境打下了基础。

通过该 API,开发者可以使用最新的密钥派生算法(如 HKDF 和未来的 Argon2):

// 创建一个 KDF 对象,使用 HKDF-SHA256 算法KDFhkdf = KDF.getInstance("HKDF-SHA256");
// 创建 Extract 和 Expand 参数规范AlgorithmParameterSpecparams =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial) // 设置初始密钥材料
.addSalt(salt) // 设置盐值
.thenExpand(info, 32); // 设置扩展信息和目标长度// 派生一个 32 字节的 AES 密钥SecretKeykey = hkdf.deriveKey("AES", params);
// 可以使用相同的 KDF 对象进行其他密钥派生操作

JEP 483: Early Class-File Loading & Linking (提前类加载和链接)

在传统 JVM 中,应用在每次启动时需要动态加载和链接类。这种机制对启动时间敏感的应用(如微服务或无服务器函数)带来了显著的性能瓶颈。该特性通过缓存已加载和链接的类,显著减少了重复工作的开销,显著减少 Java 应用程序的启动时间。测试表明,对大型应用(如基于 Spring 的服务器应用),启动时间可减少 40% 以上。

这个优化是零侵入性的,对应用程序、库或框架的代码无需任何更改,启动也方式保持一致,仅需添加相关 JVM 参数(如 -XX:+ClassDataSharing)。

JEP 484: Class File API (类文件 API)

类文件 API 在 JDK 22 进行了第一次预览(JEP 457),在 JDK 23 进行了第二次预览并进一步完善(JEP 466)。最终,该特性在 JDK 24 中顺利转正。

类文件 API 的目标是提供一套标准化的 API,用于解析、生成和转换 Java 类文件,取代过去对第三方库(如 ASM)在类文件处理上的依赖。

// 创建一个 ClassFile 对象,这是操作类文件的入口。ClassFilecf = ClassFile.of();
// 解析字节数组为 ClassModelClassModelclassModel = cf.parse(bytes);
// 构建新的类文件,移除以 "debug" 开头的所有方法byte[] newBytes = cf.build(classModel.thisClass().asSymbol(),
classBuilder -> {
// 遍历所有类元素for (ClassElementce : classModel) {
// 判断是否为方法 且 方法名以 "debug" 开头if (!(ceinstanceofMethodModelmm
&& mm.methodName().stringValue().startsWith("debug"))) {
// 添加到新的类文件中classBuilder.with(ce);
}
}
});

JEP 485: Stream Gatherers (流收集器)

流收集器 Stream::gather(Gatherer) 是一个强大的新特性,它允许开发者定义自定义的中间操作,从而实现更复杂、更灵活的数据转换。Gatherer 接口是该特性的核心,它定义了如何从流中收集元素,维护中间状态,并在处理过程中生成结果。

与现有的 filtermapdistinct 等内置操作不同,Stream::gather 使得开发者能够实现那些难以用标准 Stream 操作完成的任务。例如,可以使用 Stream::gather 实现滑动窗口、自定义规则的去重、或者更复杂的状态转换和聚合。 这种灵活性极大地扩展了 Stream API 的应用范围,使开发者能够应对更复杂的数据处理场景。

基于 Stream::gather(Gatherer) 实现字符串长度的去重逻辑:

varresult = Stream.of("foo", "bar", "baz", "quux")
.gather(Gatherer.ofSequential(
HashSet::new, // 初始化状态为 HashSet,用于保存已经遇到过的字符串长度
(set, str, downstream) -> {
if (set.add(str.length())) {
returndownstream.push(str);
}
returntrue; // 继续处理流
}
))
.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)可以在线程内和线程间共享不可变的数据,优于线程局部变量,尤其是在使用大量虚拟线程时。

finalstaticScopedValue<...> V = newScopedValue<>();
// In some methodScopedValue.where(V, <value>)
.run(() -> { ... V.get() ... callmethods ... });
// 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 文件是 Java 模块的描述文件,包含了模块的元数据和资源。

JEP 495: Simplified Source Files and Instance Main Methods (简化的源文件和实例主方法, 第四次预览)

这个特性主要简化了 main 方法的声明。对于 Java 初学者来说,这个 main 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。

没有使用该特性之前定义一个 main 方法:

publicclassHelloWorld {
publicstaticvoidmain(String[] args) {
System.out.println("Hello, World!");
}
}

使用该新特性之后定义一个 main 方法:

classHelloWorld {
voidmain() {
System.out.println("Hello, World!");
}
}

进一步简化(未命名的类允许我们省略类名)

voidmain() {
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) 提议弃用 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 协同工作。

这两个类是 Foreign Function & Memory API(外部函数和内存 API) 的核心组件,分别用于管理和操作堆外内存。Foreign Function & Memory API 在 JDK 22 中正式转正,成为标准特性。

importjdk.incubator.foreign.*;
importjava.lang.invoke.VarHandle;
// 管理堆外整数数组的类classOffHeapIntBuffer {
// 用于访问整数元素的VarHandleprivatestaticfinalVarHandleELEM_VH = ValueLayout.JAVA_INT.arrayElementVarHandle();
// 内存管理器privatefinalArenaarena;
// 堆外内存段privatefinalMemorySegmentbuffer;
// 构造函数,分配指定数量的整数空间publicOffHeapIntBuffer(longsize) {
this.arena = Arena.ofShared();
this.buffer = arena.allocate(ValueLayout.JAVA_INT, size);
}
// 释放内存publicvoiddeallocate() {
arena.close();
}
// 以volatile方式设置指定索引的值publicvoidsetVolatile(longindex, intvalue) {
ELEM_VH.setVolatile(buffer, 0L, index, value);
}
// 初始化指定范围的元素为0publicvoidinitialize(longstart, longn) {
buffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.fill((byte) 0);
}
// 将指定范围的元素复制到新数组publicint[] copyToNewArray(longstart, intn) {
returnbuffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.toArray(ValueLayout.JAVA_INT);
}
}

JEP 499: Structured Concurrency (结构化并发, 第四次预览)

JDK 19 引入了结构化并发,一种多线程编程方法,目的是为了通过结构化并发 API 来简化多线程编程,并不是为了取代java.util.concurrent,目前处于孵化器阶段。

结构化并发将不同线程中运行的多个任务视为单个工作单元,从而简化错误处理、提高可靠性并增强可观察性。也就是说,结构化并发保留了单线程代码的可读性、可维护性和可观察性。

结构化并发的基本 API 是StructuredTaskScope,它支持将任务拆分为多个并发子任务,在它们自己的线程中执行,并且子任务必须在主任务继续之前完成。

StructuredTaskScope 的基本用法如下:

try (varscope = newStructuredTaskScope<Object>()) {
// 使用fork方法派生线程来执行子任务Future<Integer> future1 = scope.fork(task1);
Future<String> future2 = scope.fork(task2);
// 等待线程完成scope.join();
// 结果的处理可能包括处理或重新抛出异常
... processresults/exceptions ...
} // close

结构化并发非常适合虚拟线程,虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程,从而允许非常多的虚拟线程。

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

History
266 lines (192 loc) · 13.6 KB

File metadata and controls

266 lines (192 loc) · 13.6 KB
titleJava 24 新特性概览
description总结 JDK 24 的新特性与改动,便于跟踪 Java 演进。
categoryJava
tag
Java新特性
head
meta
namecontent
keywords
Java 24,JDK24,JEP 更新,语言特性,GC 改进,平台增强

JDK 24 于 2025 年 3 月发布,这是一个非 LTS(长期支持版)版本。下一个长期支持版是 JDK 25,预计于 2025 年 9 月发布。

JDK 24 共有 24 个新特性,这篇文章会挑选其中较为重要的一些新特性进行详细介绍:

下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间:

JEP 478: Key Derivation Function API (密钥派生函数 API)

密钥派生函数 API 是一种用于从初始密钥和其他数据派生额外密钥的加密算法。它的核心作用是为不同的加密目的(如加密、认证等)生成多个不同的密钥,避免密钥重复使用带来的安全隐患。这在新代加密中是一个重要的里程碑,为后续新兴的量子计算环境打下了基础。

通过该 API,开发者可以使用最新的密钥派生算法(如 HKDF 和未来的 Argon2):

// 创建一个 KDF 对象,使用 HKDF-SHA256 算法KDFhkdf = KDF.getInstance("HKDF-SHA256");
// 创建 Extract 和 Expand 参数规范AlgorithmParameterSpecparams =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial) // 设置初始密钥材料
.addSalt(salt) // 设置盐值
.thenExpand(info, 32); // 设置扩展信息和目标长度// 派生一个 32 字节的 AES 密钥SecretKeykey = hkdf.deriveKey("AES", params);
// 可以使用相同的 KDF 对象进行其他密钥派生操作

JEP 483: Early Class-File Loading & Linking (提前类加载和链接)

在传统 JVM 中,应用在每次启动时需要动态加载和链接类。这种机制对启动时间敏感的应用(如微服务或无服务器函数)带来了显著的性能瓶颈。该特性通过缓存已加载和链接的类,显著减少了重复工作的开销,显著减少 Java 应用程序的启动时间。测试表明,对大型应用(如基于 Spring 的服务器应用),启动时间可减少 40% 以上。

这个优化是零侵入性的,对应用程序、库或框架的代码无需任何更改,启动也方式保持一致,仅需添加相关 JVM 参数(如 -XX:+ClassDataSharing)。

JEP 484: Class File API (类文件 API)

类文件 API 在 JDK 22 进行了第一次预览(JEP 457),在 JDK 23 进行了第二次预览并进一步完善(JEP 466)。最终,该特性在 JDK 24 中顺利转正。

类文件 API 的目标是提供一套标准化的 API,用于解析、生成和转换 Java 类文件,取代过去对第三方库(如 ASM)在类文件处理上的依赖。

// 创建一个 ClassFile 对象,这是操作类文件的入口。ClassFilecf = ClassFile.of();
// 解析字节数组为 ClassModelClassModelclassModel = cf.parse(bytes);
// 构建新的类文件,移除以 "debug" 开头的所有方法byte[] newBytes = cf.build(classModel.thisClass().asSymbol(),
classBuilder -> {
// 遍历所有类元素for (ClassElementce : classModel) {
// 判断是否为方法 且 方法名以 "debug" 开头if (!(ceinstanceofMethodModelmm
&& mm.methodName().stringValue().startsWith("debug"))) {
// 添加到新的类文件中classBuilder.with(ce);
}
}
});

JEP 485: Stream Gatherers (流收集器)

流收集器 Stream::gather(Gatherer) 是一个强大的新特性,它允许开发者定义自定义的中间操作,从而实现更复杂、更灵活的数据转换。Gatherer 接口是该特性的核心,它定义了如何从流中收集元素,维护中间状态,并在处理过程中生成结果。

与现有的 filtermapdistinct 等内置操作不同,Stream::gather 使得开发者能够实现那些难以用标准 Stream 操作完成的任务。例如,可以使用 Stream::gather 实现滑动窗口、自定义规则的去重、或者更复杂的状态转换和聚合。 这种灵活性极大地扩展了 Stream API 的应用范围,使开发者能够应对更复杂的数据处理场景。

基于 Stream::gather(Gatherer) 实现字符串长度的去重逻辑:

varresult = Stream.of("foo", "bar", "baz", "quux")
.gather(Gatherer.ofSequential(
HashSet::new, // 初始化状态为 HashSet,用于保存已经遇到过的字符串长度
(set, str, downstream) -> {
if (set.add(str.length())) {
returndownstream.push(str);
}
returntrue; // 继续处理流
}
))
.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)可以在线程内和线程间共享不可变的数据,优于线程局部变量,尤其是在使用大量虚拟线程时。

finalstaticScopedValue<...> V = newScopedValue<>();
// In some methodScopedValue.where(V, <value>)
.run(() -> { ... V.get() ... callmethods ... });
// 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 文件是 Java 模块的描述文件,包含了模块的元数据和资源。

JEP 495: Simplified Source Files and Instance Main Methods (简化的源文件和实例主方法, 第四次预览)

这个特性主要简化了 main 方法的声明。对于 Java 初学者来说,这个 main 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。

没有使用该特性之前定义一个 main 方法:

publicclassHelloWorld {
publicstaticvoidmain(String[] args) {
System.out.println("Hello, World!");
}
}

使用该新特性之后定义一个 main 方法:

classHelloWorld {
voidmain() {
System.out.println("Hello, World!");
}
}

进一步简化(未命名的类允许我们省略类名)

voidmain() {
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) 提议弃用 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 协同工作。

这两个类是 Foreign Function & Memory API(外部函数和内存 API) 的核心组件,分别用于管理和操作堆外内存。Foreign Function & Memory API 在 JDK 22 中正式转正,成为标准特性。

importjdk.incubator.foreign.*;
importjava.lang.invoke.VarHandle;
// 管理堆外整数数组的类classOffHeapIntBuffer {
// 用于访问整数元素的VarHandleprivatestaticfinalVarHandleELEM_VH = ValueLayout.JAVA_INT.arrayElementVarHandle();
// 内存管理器privatefinalArenaarena;
// 堆外内存段privatefinalMemorySegmentbuffer;
// 构造函数,分配指定数量的整数空间publicOffHeapIntBuffer(longsize) {
this.arena = Arena.ofShared();
this.buffer = arena.allocate(ValueLayout.JAVA_INT, size);
}
// 释放内存publicvoiddeallocate() {
arena.close();
}
// 以volatile方式设置指定索引的值publicvoidsetVolatile(longindex, intvalue) {
ELEM_VH.setVolatile(buffer, 0L, index, value);
}
// 初始化指定范围的元素为0publicvoidinitialize(longstart, longn) {
buffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.fill((byte) 0);
}
// 将指定范围的元素复制到新数组publicint[] copyToNewArray(longstart, intn) {
returnbuffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.toArray(ValueLayout.JAVA_INT);
}
}

JEP 499: Structured Concurrency (结构化并发, 第四次预览)

JDK 19 引入了结构化并发,一种多线程编程方法,目的是为了通过结构化并发 API 来简化多线程编程,并不是为了取代java.util.concurrent,目前处于孵化器阶段。

结构化并发将不同线程中运行的多个任务视为单个工作单元,从而简化错误处理、提高可靠性并增强可观察性。也就是说,结构化并发保留了单线程代码的可读性、可维护性和可观察性。

结构化并发的基本 API 是StructuredTaskScope,它支持将任务拆分为多个并发子任务,在它们自己的线程中执行,并且子任务必须在主任务继续之前完成。

StructuredTaskScope 的基本用法如下:

try (varscope = newStructuredTaskScope<Object>()) {
// 使用fork方法派生线程来执行子任务Future<Integer> future1 = scope.fork(task1);
Future<String> future2 = scope.fork(task2);
// 等待线程完成scope.join();
// 结果的处理可能包括处理或重新抛出异常
... processresults/exceptions ...
} // close

结构化并发非常适合虚拟线程,虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程,从而允许非常多的虚拟线程。

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

History
266 lines (192 loc) · 13.6 KB

File metadata and controls

266 lines (192 loc) · 13.6 KB
titleJava 24 新特性概览
description总结 JDK 24 的新特性与改动,便于跟踪 Java 演进。
categoryJava
tag
Java新特性
head
meta
namecontent
keywords
Java 24,JDK24,JEP 更新,语言特性,GC 改进,平台增强

JDK 24 于 2025 年 3 月发布,这是一个非 LTS(长期支持版)版本。下一个长期支持版是 JDK 25,预计于 2025 年 9 月发布。

JDK 24 共有 24 个新特性,这篇文章会挑选其中较为重要的一些新特性进行详细介绍:

下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间:

JEP 478: Key Derivation Function API (密钥派生函数 API)

密钥派生函数 API 是一种用于从初始密钥和其他数据派生额外密钥的加密算法。它的核心作用是为不同的加密目的(如加密、认证等)生成多个不同的密钥,避免密钥重复使用带来的安全隐患。这在新代加密中是一个重要的里程碑,为后续新兴的量子计算环境打下了基础。

通过该 API,开发者可以使用最新的密钥派生算法(如 HKDF 和未来的 Argon2):

// 创建一个 KDF 对象,使用 HKDF-SHA256 算法KDFhkdf = KDF.getInstance("HKDF-SHA256");
// 创建 Extract 和 Expand 参数规范AlgorithmParameterSpecparams =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial) // 设置初始密钥材料
.addSalt(salt) // 设置盐值
.thenExpand(info, 32); // 设置扩展信息和目标长度// 派生一个 32 字节的 AES 密钥SecretKeykey = hkdf.deriveKey("AES", params);
// 可以使用相同的 KDF 对象进行其他密钥派生操作

JEP 483: Early Class-File Loading & Linking (提前类加载和链接)

在传统 JVM 中,应用在每次启动时需要动态加载和链接类。这种机制对启动时间敏感的应用(如微服务或无服务器函数)带来了显著的性能瓶颈。该特性通过缓存已加载和链接的类,显著减少了重复工作的开销,显著减少 Java 应用程序的启动时间。测试表明,对大型应用(如基于 Spring 的服务器应用),启动时间可减少 40% 以上。

这个优化是零侵入性的,对应用程序、库或框架的代码无需任何更改,启动也方式保持一致,仅需添加相关 JVM 参数(如 -XX:+ClassDataSharing)。

JEP 484: Class File API (类文件 API)

类文件 API 在 JDK 22 进行了第一次预览(JEP 457),在 JDK 23 进行了第二次预览并进一步完善(JEP 466)。最终,该特性在 JDK 24 中顺利转正。

类文件 API 的目标是提供一套标准化的 API,用于解析、生成和转换 Java 类文件,取代过去对第三方库(如 ASM)在类文件处理上的依赖。

// 创建一个 ClassFile 对象,这是操作类文件的入口。ClassFilecf = ClassFile.of();
// 解析字节数组为 ClassModelClassModelclassModel = cf.parse(bytes);
// 构建新的类文件,移除以 "debug" 开头的所有方法byte[] newBytes = cf.build(classModel.thisClass().asSymbol(),
classBuilder -> {
// 遍历所有类元素for (ClassElementce : classModel) {
// 判断是否为方法 且 方法名以 "debug" 开头if (!(ceinstanceofMethodModelmm
&& mm.methodName().stringValue().startsWith("debug"))) {
// 添加到新的类文件中classBuilder.with(ce);
}
}
});

JEP 485: Stream Gatherers (流收集器)

流收集器 Stream::gather(Gatherer) 是一个强大的新特性,它允许开发者定义自定义的中间操作,从而实现更复杂、更灵活的数据转换。Gatherer 接口是该特性的核心,它定义了如何从流中收集元素,维护中间状态,并在处理过程中生成结果。

与现有的 filtermapdistinct 等内置操作不同,Stream::gather 使得开发者能够实现那些难以用标准 Stream 操作完成的任务。例如,可以使用 Stream::gather 实现滑动窗口、自定义规则的去重、或者更复杂的状态转换和聚合。 这种灵活性极大地扩展了 Stream API 的应用范围,使开发者能够应对更复杂的数据处理场景。

基于 Stream::gather(Gatherer) 实现字符串长度的去重逻辑:

varresult = Stream.of("foo", "bar", "baz", "quux")
.gather(Gatherer.ofSequential(
HashSet::new, // 初始化状态为 HashSet,用于保存已经遇到过的字符串长度
(set, str, downstream) -> {
if (set.add(str.length())) {
returndownstream.push(str);
}
returntrue; // 继续处理流
}
))
.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)可以在线程内和线程间共享不可变的数据,优于线程局部变量,尤其是在使用大量虚拟线程时。

finalstaticScopedValue<...> V = newScopedValue<>();
// In some methodScopedValue.where(V, <value>)
.run(() -> { ... V.get() ... callmethods ... });
// 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 文件是 Java 模块的描述文件,包含了模块的元数据和资源。

JEP 495: Simplified Source Files and Instance Main Methods (简化的源文件和实例主方法, 第四次预览)

这个特性主要简化了 main 方法的声明。对于 Java 初学者来说,这个 main 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。

没有使用该特性之前定义一个 main 方法:

publicclassHelloWorld {
publicstaticvoidmain(String[] args) {
System.out.println("Hello, World!");
}
}

使用该新特性之后定义一个 main 方法:

classHelloWorld {
voidmain() {
System.out.println("Hello, World!");
}
}

进一步简化(未命名的类允许我们省略类名)

voidmain() {
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) 提议弃用 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 协同工作。

这两个类是 Foreign Function & Memory API(外部函数和内存 API) 的核心组件,分别用于管理和操作堆外内存。Foreign Function & Memory API 在 JDK 22 中正式转正,成为标准特性。

importjdk.incubator.foreign.*;
importjava.lang.invoke.VarHandle;
// 管理堆外整数数组的类classOffHeapIntBuffer {
// 用于访问整数元素的VarHandleprivatestaticfinalVarHandleELEM_VH = ValueLayout.JAVA_INT.arrayElementVarHandle();
// 内存管理器privatefinalArenaarena;
// 堆外内存段privatefinalMemorySegmentbuffer;
// 构造函数,分配指定数量的整数空间publicOffHeapIntBuffer(longsize) {
this.arena = Arena.ofShared();
this.buffer = arena.allocate(ValueLayout.JAVA_INT, size);
}
// 释放内存publicvoiddeallocate() {
arena.close();
}
// 以volatile方式设置指定索引的值publicvoidsetVolatile(longindex, intvalue) {
ELEM_VH.setVolatile(buffer, 0L, index, value);
}
// 初始化指定范围的元素为0publicvoidinitialize(longstart, longn) {
buffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.fill((byte) 0);
}
// 将指定范围的元素复制到新数组publicint[] copyToNewArray(longstart, intn) {
returnbuffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.toArray(ValueLayout.JAVA_INT);
}
}

JEP 499: Structured Concurrency (结构化并发, 第四次预览)

JDK 19 引入了结构化并发,一种多线程编程方法,目的是为了通过结构化并发 API 来简化多线程编程,并不是为了取代java.util.concurrent,目前处于孵化器阶段。

结构化并发将不同线程中运行的多个任务视为单个工作单元,从而简化错误处理、提高可靠性并增强可观察性。也就是说,结构化并发保留了单线程代码的可读性、可维护性和可观察性。

结构化并发的基本 API 是StructuredTaskScope,它支持将任务拆分为多个并发子任务,在它们自己的线程中执行,并且子任务必须在主任务继续之前完成。

StructuredTaskScope 的基本用法如下:

try (varscope = newStructuredTaskScope<Object>()) {
// 使用fork方法派生线程来执行子任务Future<Integer> future1 = scope.fork(task1);
Future<String> future2 = scope.fork(task2);
// 等待线程完成scope.join();
// 结果的处理可能包括处理或重新抛出异常
... processresults/exceptions ...
} // close

结构化并发非常适合虚拟线程,虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程,从而允许非常多的虚拟线程。

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

History
266 lines (192 loc) · 13.6 KB

File metadata and controls

266 lines (192 loc) · 13.6 KB
titleJava 24 新特性概览
description总结 JDK 24 的新特性与改动,便于跟踪 Java 演进。
categoryJava
tag
Java新特性
head
meta
namecontent
keywords
Java 24,JDK24,JEP 更新,语言特性,GC 改进,平台增强

JDK 24 于 2025 年 3 月发布,这是一个非 LTS(长期支持版)版本。下一个长期支持版是 JDK 25,预计于 2025 年 9 月发布。

JDK 24 共有 24 个新特性,这篇文章会挑选其中较为重要的一些新特性进行详细介绍:

下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间:

JEP 478: Key Derivation Function API (密钥派生函数 API)

密钥派生函数 API 是一种用于从初始密钥和其他数据派生额外密钥的加密算法。它的核心作用是为不同的加密目的(如加密、认证等)生成多个不同的密钥,避免密钥重复使用带来的安全隐患。这在新代加密中是一个重要的里程碑,为后续新兴的量子计算环境打下了基础。

通过该 API,开发者可以使用最新的密钥派生算法(如 HKDF 和未来的 Argon2):

// 创建一个 KDF 对象,使用 HKDF-SHA256 算法KDFhkdf = KDF.getInstance("HKDF-SHA256");
// 创建 Extract 和 Expand 参数规范AlgorithmParameterSpecparams =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial) // 设置初始密钥材料
.addSalt(salt) // 设置盐值
.thenExpand(info, 32); // 设置扩展信息和目标长度// 派生一个 32 字节的 AES 密钥SecretKeykey = hkdf.deriveKey("AES", params);
// 可以使用相同的 KDF 对象进行其他密钥派生操作

JEP 483: Early Class-File Loading & Linking (提前类加载和链接)

在传统 JVM 中,应用在每次启动时需要动态加载和链接类。这种机制对启动时间敏感的应用(如微服务或无服务器函数)带来了显著的性能瓶颈。该特性通过缓存已加载和链接的类,显著减少了重复工作的开销,显著减少 Java 应用程序的启动时间。测试表明,对大型应用(如基于 Spring 的服务器应用),启动时间可减少 40% 以上。

这个优化是零侵入性的,对应用程序、库或框架的代码无需任何更改,启动也方式保持一致,仅需添加相关 JVM 参数(如 -XX:+ClassDataSharing)。

JEP 484: Class File API (类文件 API)

类文件 API 在 JDK 22 进行了第一次预览(JEP 457),在 JDK 23 进行了第二次预览并进一步完善(JEP 466)。最终,该特性在 JDK 24 中顺利转正。

类文件 API 的目标是提供一套标准化的 API,用于解析、生成和转换 Java 类文件,取代过去对第三方库(如 ASM)在类文件处理上的依赖。

// 创建一个 ClassFile 对象,这是操作类文件的入口。ClassFilecf = ClassFile.of();
// 解析字节数组为 ClassModelClassModelclassModel = cf.parse(bytes);
// 构建新的类文件,移除以 "debug" 开头的所有方法byte[] newBytes = cf.build(classModel.thisClass().asSymbol(),
classBuilder -> {
// 遍历所有类元素for (ClassElementce : classModel) {
// 判断是否为方法 且 方法名以 "debug" 开头if (!(ceinstanceofMethodModelmm
&& mm.methodName().stringValue().startsWith("debug"))) {
// 添加到新的类文件中classBuilder.with(ce);
}
}
});

JEP 485: Stream Gatherers (流收集器)

流收集器 Stream::gather(Gatherer) 是一个强大的新特性,它允许开发者定义自定义的中间操作,从而实现更复杂、更灵活的数据转换。Gatherer 接口是该特性的核心,它定义了如何从流中收集元素,维护中间状态,并在处理过程中生成结果。

与现有的 filtermapdistinct 等内置操作不同,Stream::gather 使得开发者能够实现那些难以用标准 Stream 操作完成的任务。例如,可以使用 Stream::gather 实现滑动窗口、自定义规则的去重、或者更复杂的状态转换和聚合。 这种灵活性极大地扩展了 Stream API 的应用范围,使开发者能够应对更复杂的数据处理场景。

基于 Stream::gather(Gatherer) 实现字符串长度的去重逻辑:

varresult = Stream.of("foo", "bar", "baz", "quux")
.gather(Gatherer.ofSequential(
HashSet::new, // 初始化状态为 HashSet,用于保存已经遇到过的字符串长度
(set, str, downstream) -> {
if (set.add(str.length())) {
returndownstream.push(str);
}
returntrue; // 继续处理流
}
))
.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)可以在线程内和线程间共享不可变的数据,优于线程局部变量,尤其是在使用大量虚拟线程时。

finalstaticScopedValue<...> V = newScopedValue<>();
// In some methodScopedValue.where(V, <value>)
.run(() -> { ... V.get() ... callmethods ... });
// 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 文件是 Java 模块的描述文件,包含了模块的元数据和资源。

JEP 495: Simplified Source Files and Instance Main Methods (简化的源文件和实例主方法, 第四次预览)

这个特性主要简化了 main 方法的声明。对于 Java 初学者来说,这个 main 方法的声明引入了太多的 Java 语法概念,不利于初学者快速上手。

没有使用该特性之前定义一个 main 方法:

publicclassHelloWorld {
publicstaticvoidmain(String[] args) {
System.out.println("Hello, World!");
}
}

使用该新特性之后定义一个 main 方法:

classHelloWorld {
voidmain() {
System.out.println("Hello, World!");
}
}

进一步简化(未命名的类允许我们省略类名)

voidmain() {
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) 提议弃用 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 协同工作。

这两个类是 Foreign Function & Memory API(外部函数和内存 API) 的核心组件,分别用于管理和操作堆外内存。Foreign Function & Memory API 在 JDK 22 中正式转正,成为标准特性。

importjdk.incubator.foreign.*;
importjava.lang.invoke.VarHandle;
// 管理堆外整数数组的类classOffHeapIntBuffer {
// 用于访问整数元素的VarHandleprivatestaticfinalVarHandleELEM_VH = ValueLayout.JAVA_INT.arrayElementVarHandle();
// 内存管理器privatefinalArenaarena;
// 堆外内存段privatefinalMemorySegmentbuffer;
// 构造函数,分配指定数量的整数空间publicOffHeapIntBuffer(longsize) {
this.arena = Arena.ofShared();
this.buffer = arena.allocate(ValueLayout.JAVA_INT, size);
}
// 释放内存publicvoiddeallocate() {
arena.close();
}
// 以volatile方式设置指定索引的值publicvoidsetVolatile(longindex, intvalue) {
ELEM_VH.setVolatile(buffer, 0L, index, value);
}
// 初始化指定范围的元素为0publicvoidinitialize(longstart, longn) {
buffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.fill((byte) 0);
}
// 将指定范围的元素复制到新数组publicint[] copyToNewArray(longstart, intn) {
returnbuffer.asSlice(ValueLayout.JAVA_INT.byteSize() * start,
ValueLayout.JAVA_INT.byteSize() * n)
.toArray(ValueLayout.JAVA_INT);
}
}

JEP 499: Structured Concurrency (结构化并发, 第四次预览)

JDK 19 引入了结构化并发,一种多线程编程方法,目的是为了通过结构化并发 API 来简化多线程编程,并不是为了取代java.util.concurrent,目前处于孵化器阶段。

结构化并发将不同线程中运行的多个任务视为单个工作单元,从而简化错误处理、提高可靠性并增强可观察性。也就是说,结构化并发保留了单线程代码的可读性、可维护性和可观察性。

结构化并发的基本 API 是StructuredTaskScope,它支持将任务拆分为多个并发子任务,在它们自己的线程中执行,并且子任务必须在主任务继续之前完成。

StructuredTaskScope 的基本用法如下:

try (varscope = newStructuredTaskScope<Object>()) {
// 使用fork方法派生线程来执行子任务Future<Integer> future1 = scope.fork(task1);
Future<String> future2 = scope.fork(task2);
// 等待线程完成scope.join();
// 结果的处理可能包括处理或重新抛出异常
... processresults/exceptions ...
} // close

结构化并发非常适合虚拟线程,虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程,从而允许非常多的虚拟线程。