Java 25是Java全新的长期支持(LTS)版本。该版本于2025年9月16日发布。它接替了两年前发布的Java 21,并引入了许多改进,特别是在简化语言(为Switch引入模式匹配、记录模式)以及使用虚拟线程进行并发管理方面。
那么,Java 25怎么样?这个版本带来了哪些新功能和改进?
让我们通过这篇关于Java 21以来所引入变更的概览来一探究竟。
Java,一门加速转型的语言
Java 25带来了众多新功能和改进,延续了先前版本的势头。自Java 21以来,几个中间版本(Java 22、23和24)引入了宝贵的功能,特别是在简化语言和优化性能方面,如减少Java应用程序的启动时间。它们还针对未来量子计算的出现,对API进行了改进并增强了安全性,因为量子计算可能会破解当今的加密算法。
自Java 21以来,总共已集成并最终确定了20多项JEP(Java增强提案)。
这些进步证实,Java在保持其简洁性、可移植性和健壮性等核心原则的同时,正继续巩固其作为现代高性能编程语言的地位。
Java语言增强
Java 25对语言进行了重大改进,旨在提高代码的可读性和可维护性,同时简化其语法。以下是自Java 21以来新增的主要特性:
JEP 456:未命名变量与模式
无论是出于编码风格的原因,还是仅仅因为Java的要求,开发人员经常会在某些上下文中声明变量,但最终并未使用它们。
例如,catch块通常以这种形式编写,其中异常参数ex并未实际使用:
String s = ...;
try {
int i = Integer.parseInt(s);
... i ...
}
catch (NumberFormatException ex) {
System.out.println("Bad number: " + s); // ex is not used
}
在Java 25中,未使用的变量可以用下划线(_)作为变量名进行声明。
例如,之前的catch块可以重写如下:
String s = ...;
try {
int i = Integer.parseInt(s);
... i ...
}
catch (NumberFormatException _) { // Unnamed variable
System.out.println("Bad number: " + s);
}
这种语法在方法参数、lambda表达式、模式匹配等中也是有效的。
JEP 467:Markdown文档注释
迄今为止,Java文档注释主要使用HTML编写,这往往使文档的编写和阅读变得更加复杂。随着JEP 467的引入,Java 25现在允许在文档注释中使用Markdown。这极大地简化了文档的创建,使开发人员能够更直观、更轻松地完成这一过程。Markdown支持还使得能够更好地与各种文档工具和开发平台集成。
JEP 511:模块导入声明
Java 25 引入了通过在源文件中使用单个导入模块声明,即可将模块中所有必需的包导入到 Java 源代码中的功能。这简化了依赖管理,并通过减少从同一模块导入多个包所需的行数,提高了代码的可读性。
Java 25之前的示例:
import java.util.Map; // or import java.util.*;
import java.util.function.Function; // or import java.util.function.*;
import java.util.stream.Collectors; // or import java.util.stream.*;
import java.util.stream.Stream; // (can be removed)
...
String[] fruits = new String[] { "apple", "berry", "citrus" };
Map<String, String> m =
Stream.of(fruits)
.collect(Collectors.toMap(s -> s.toUpperCase().substring(0,1),
Function.identity()));
Java 25示例:
import module java.base; // Import all exported packages from java.base
...
String[] fruits = new String[] { "apple", "berry", "citrus" };
Map<String, String> m =
Stream.of(fruits)
.collect(Collectors.toMap(s -> s.toUpperCase().substring(0,1),
Function.identity()));
JEP 512:紧凑源代码文件和实例主方法
与其他语言相比,Java的语法相当冗长。为了简化其语法,现在可以用更紧凑的形式编写Java代码。这一演变对于Java新手来说尤其有用,因为它使他们能够更容易地熟悉该语言,而不必立即深入学习类、包、模块或可见性修饰符等更复杂的概念,这些概念在学习初期可能会令人困惑。它特别适合以简洁、类似脚本的风格编写Java程序。
这一特性使得以下操作成为可能:
- 采用紧凑形式的源文件,不声明多余的类 用更简单、更易理解的实例
main()方法 - 替换传统的公共静态
void main(String[] args)方法 - 在java.lang包中包含一个新类,该类提供简化和更适合初学者的I/O方法(而不是经典的
System.out.println) - 当代码以紧凑格式编写时(见第1点),自动导入
java.lang包中的标准API
Java 25之前的示例:
class HelloWorld {
void main() {
System.out.println("Hello, World!");
}
}
Java 25示例:
main() {
IO.println("Hello, World! in compact style");
}
JEP 513:灵活的构造函数体
在之前的LTS版本中,无法在调用父类构造函数(super())之前编写语句。在某些情况下,这可能会导致不必要的调用,尤其是在初始化对象之前需要进行参数验证时。
在Java 25中,该语言现在允许在super()调用之前编写语句,从而在管理对象初始化方面提供了更大的灵活性。
演示此增强的示例代码:
public class PositiveBigInteger extends BigInteger {
private final long max;
public PositiveBigInteger(long value, long max) {
// Code before calling super()
if (value <= 0)
throw new IllegalArgumentException("non-positive value");
this.max = max;
// Call super() last : Avoids unnecessary calls to the parent constructor if an exception is raised upstream (not possible before)
super(value);
}
}
Java 25的性能提升
Java 25在性能提升方面迈出了重要一步,尤其是整合了Leyden项目的先进技术。
Leyden项目的目标是减少Java应用程序的启动时间和内存占用,从而提高其性能,尤其是在云原生和无服务器环境中。迄今为止,实现近乎即刻启动的主要解决方案是使用GraalVM Native Image编译的本机应用程序,这些应用程序可以在几毫秒内启动。然而,这种方法存在几个局限性:编译时间长且成本高,对反射和某些库的支持有限,有时与传统JVM相比吞吐量较低。
在Java 25中,OpenJDK旨在结合JVM的优点(兼容性、动态优化、成熟的生态系统)以及显著更快的启动时间,这得益于引入了提前编译部分代码的预编译缓存机制。
Java 25中的提前编译(AOT)缓存(JEPs 483、514和515)
通过引入提前编译(AOT)缓存显著提高了应用程序的性能。
通过JEPs 483和514引入的AOT缓存显著缩短了Java应用程序的启动时间。该缓存是在应用程序的训练运行期间构建的,它会记录所使用的类和方法,然后为这些类和方法生成优化的AOT缓存。在应用程序启动时,此缓存会被加载到内存中,从而减少即时(Just-In-Time,JIT)编译的开销,并提高整体启动性能。
JEP 515 通过整合 JIT 收集的性能分析数据,进一步增强了 AOT 缓存。这使得缓存更加高效,因为它基于应用程序的实际运行时行为进行了优化。
在拥有大量类和方法的应用程序中,性能提升尤为显著,启动时间可大幅缩短。根据应用程序的不同,启动速度可提升多达50%。
要构建AOT缓存,您需要添加以下选项:
$ java -XX:AOTCacheOutput=app.aot -cp app.jar com.example.App ...
要使用之前构建的AOT缓存运行应用程序,您需要添加以下选项:
$ java -XX:AOTCache=app.aot -cp app.jar com.example.App ...
JEP 519:紧凑对象头改进
对对象头管理进行了优化,以减小其大小。通过优化对象头的结构,Java 25 降低了应用程序的内存占用——这一优势对于在内存中管理大量对象的应用程序尤其宝贵。此外,这种优化还通过减轻垃圾收集器(GC)的压力,有助于整体性能的提升。
为了验证这一特性的稳定性,甲骨文和亚马逊在数百个生产服务中进行了广泛测试。结果显示,在某些情况下,内存使用率降低了多达20%,处理时间减少了多达10%,垃圾收集(GC)周期减少了多达15%。
此选项默认已禁用。若要启用,您需要添加以下JVM选项:-XX:+CompactObjectHeaders
垃圾收集器(GC)
演进 Java提供了多种垃圾收集器(GC)以满足应用程序的不同需求。在Java 25中,对G1、ZGC和Shenandoah收集器进行了改进。
Shenandoah是一种低延迟垃圾收集器(GC),旨在通过在应用程序执行的同时执行大部分收集工作,从而最大限度地减少应用程序暂停。Shenandoah并非Java中的默认垃圾收集器;它更适合具有超大堆内存的应用程序,如大数据工作负载。通过JEP 521,新增了一种分代模式。默认的“非分代”模式仍然可用。要启用Shenandoah的分代模式,请在JVM中添加以下选项:-XX:ShenandoahGCMode=generational
通过JEP 474,ZGC垃圾收集器也增加了分代模式,从而弃用了非分代模式。由于分代模式通常比非分代模式性能更高,因此未来的开发将聚焦于这一方法。
最后,Java 25的默认垃圾收集器G1也通过JEP 423得到了改进。这些改进旨在减少涉及JNI(Java Native Interface,Java本地接口)函数操作时的G1暂停时间。
Java 25:API增强
Java标准库随着Java 25的发布而不断发展,旨在为开发者提供更多功能。
JEP 502:作用域值
作用域值是一种容器,它允许在同一线程内的方法及其调用者之间,以及与子线程之间高效共享不可变数据,而无需依赖方法参数。与变量不同,作用域值(ScopedValue)只需定义一次,即可在明确规定的执行范围内保持可读状态。
作用域值在虚拟线程环境中特别有用,因为与传统ThreadLocal相比,它们提供了更低的内存和性能开销。虚拟线程轻量级且生命周期短,应用程序可以运行数百万个虚拟线程。在此环境中使用ThreadLocal会导致内存使用过度,如果清理不当甚至会导致内存泄漏。作用域值的设计考虑了虚拟线程,但它们也适用于传统线程。
以下示例展示了一个将已连接用户定义为作用域值的API:
public class ScopedValuesExample {
// Logger
private static final Logger log = Logger.getLogger(ScopedValuesExample.class.getName());
// Declaring a ScopedValue
private static final ScopedValue<String> USERNAME = ScopedValue.newInstance();
public static void main(String[] args) {
// Define the scope with a value
ScopedValue.where(USERNAME, "Alice").run(() -> {
log.info("In the principal scope");
methodA(); // Can access USERNAME without parameters
}); // USERNAME is no longer accessible here
// Attempt to access outside scope (raises an exception)
try {
String user = USERNAME.get(); // Exception !
} catch (NoSuchElementException e) {
log.info("USERNAME is not accessible outside the scope");
}
}
private static void methodA() {
// Direct access to the value from any method within the scope
String user = USERNAME.get();
log.info("Method A - User: " + user);
methodB(); // Calling a deeper method
}
private static void methodB() {
// The value remains accessible even in nested calls
String user = USERNAME.get();
log.info("Method B - User: " + user);
}
}
JEP 485:流收集器
流收集器是对流API的增强。收集器使得为流创建自定义中间操作成为可能。值得注意的是,通过Collector接口已经可以创建自定义终端操作。
以下示例重现了中间操作Gatherers.limit():
Gatherer<String, ?, String> limit() {
class Counter {
int counter;
Counter(int counter) { this.counter = counter; }
}
return Gatherer.ofSequential(
() -> new Counter(0),
(counter, element, downstream) -> {
if (counter.counter++ == 3) {
return false;
}
return downstream.push(element);
}
);
}
然而,创建自定义中间操作来重现已有功能(如上文中的limit()示例)效率较低。这些自定义操作无法从JIT编译器的所有优化中受益。
JEP 491:在不锁定虚拟线程的情况下同步虚拟线程
是Java 21中引入的主要创新,并且其与Java生态系统的集成已得到进一步优化。此功能旨在实现两个主要目标:
- 易于采用——使现有的Java库能更轻松地与虚拟线程协同工作,无需进行更改以避免使用同步方法或同步块。
- 优化诊断——增强诊断工具,以更有效地识别虚拟线程与平台线程绑定的情况,这种情况会阻止虚拟线程被释放。
虚拟线程仍是Java 21的代表性改进,在Java 25中,其与生态系统的集成得到了进一步提升。
JEP 484:Class-File API
通过这一增强功能,现在可以在运行时动态生成Java类。这为高级用例打开了大门,如即时代码生成、动态代理创建以及其他需要灵活性和动态性的场景。
以下是一个使用Class-File API生成Java类的简单示例:
ClassBuilder classBuilder = ...;
classBuilder.withMethod(
"fooBar",
MethodTypeDesc.of(CD_void, CD_boolean, CD_int),
flags,
methodBuilder -> methodBuilder.withCode(
codeBuilder -> {
Label label1 = codeBuilder.newLabel();
Label label2 = codeBuilder.newLabel();
codeBuilder.iload(1)
.ifeq(label1)
.aload(0)
.iload(2)
.invokevirtual(ClassDesc.of("Foo"), "foo", MethodTypeDesc.of(CD_void, CD_int))
.goto_(label2)
.labelBinding(label1)
.aload(0)
.iload(2)
.invokevirtual(ClassDesc.of("Foo"), "bar", MethodTypeDesc.of(CD_void, CD_int))
.labelBinding(label2);
.return_();
}
)
);
生成的代码等同于以下Java代码:
void fooBar(boolean z, int x) {
if (z)
foo(x);
else
bar(x);
}
JEP 454:外部函数与内存API
外部函数与内存(FFM)API的创建现在提供了对JVM外部管理的代码和数据的访问。此API支持安全调用本机代码(C、C++等)以及访问JVM堆栈之外的内存块。
以下代码展示了一个使用FFM API调用C函数strlen的简单示例,该函数用于计算字符串的长度:
public class FFMTest {
public static void main(String[] args) throws Throwable {
// 1. Get a lookup object for commonly used libraries
SymbolLookup stdlib = Linker.nativeLinker().defaultLookup();
// 2. Get a handle to the "strlen" function in the C standard library
MethodHandle strlen =
Linker.nativeLinker()
.downcallHandle(
stdlib.find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
// 3. Get a confined memory area (one that we can close explicitly)
try (Arena offHeap = Arena.ofConfined()) {
// 4. Convert the Java String to a C string and store it in off-heap memory
MemorySegment str = offHeap.allocateFrom("Happy Coding!");
// 5. Invoke the foreign function
long len = (long) strlen.invoke(str);
System.out.println("len = " + len);
}
// 6. Off-heap memory is deallocated at end of try-with-resources
}
}
JEP 472:准备限制JNI的使用
Java 25仍然允许通过Java Native Interface (JNI)或Foreign Function & Memory (FFM) API调用本机代码。由于FFM API中的本机代码加载受到限制,默认情况下会发出运行时警告。
为了确保JNI和FFM之间的行为一致,在执行本机代码时,使用JNI也会触发一个警告。
使用 JNI 或 FFM API 时显示的消息:
WARNING: A restricted method in java.lang.foreign.Linker has been called
WARNING: java.lang.foreign.Linker::downcallHandle has been called by com.example.Main in an unnamed module
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
要禁用所有Java代码的警告消息,您可以使用以下选项:
java --enable-native-access=ALL-UNNAMED ...
sun.misc.Unsafe方法弃用 JEP 471和498旨在为移除sun.misc.Unsafe API(2002年引入,计划从Java 26开始完全移除)提供的内存访问方法做好生态系统准备。此API中的方法被视为危险方法,因为它们允许直接访问内存,这可能导致安全和应用程序稳定性问题。
建议将对此API的调用替换为Java 25中引入的新外部函数与内存(FFM)API(见上文JEP 454)。
每当应用程序直接或间接使用这些方法时,都会发出警告。
JEP 458:启动多文件源代码程序
自Java 11起,无需事先编译步骤,即可直接从.java源文件运行程序。Java启动器会在执行前自动在内存中编译程序。
JEP 458 更进一步,它支持简化依赖于额外源文件的Java程序的执行。它允许启动一个使用另一个源文件中定义的类的源文件,同时后者也会在内存中自动编译。源文件的搜索遵循通常的Java目录层次结构,该结构反映了包的结构。
只有主程序实际使用的源文件才会在内存中进行编译。
JEP 486:永久禁用安全管理器
安全管理器很少用于保护服务器端代码,且维护成本高昂。此功能在 Java 17 中已被弃用。
由于安全管理器的采用率较低,且大多数框架和工具已停止对其支持,安全管理器现已被永久禁用,在Java 25中不再可用。
JEP 503:移除32位x86端口支持
在Java 25中已移除对32位x86架构的32位x86端口支持。
与实际使用情况相比,这种架构的维护成本已经变得过高。鉴于目前绝大多数现代系统都运行在64位架构上,继续支持32位已变得越来越无足轻重。
Java为后量子时代做好准备
鉴于量子计算未来的发展——这可能会使当今的加密算法变得脆弱——Java必须开始为未来几年内集成混合、抗量子加密机制奠定基础。
借助JEP 510,Java现在支持RFC 5869中定义的HKDF(基于HMAC的密钥派生函数)算法。该算法使用底层加密哈希函数,从初始密钥和可选盐值中派生出秘密密钥。
使用HKDF-SHA256创建派生密钥的示例:
// Create a KDF object for the specified algorithm
KDF hkdf = KDF.getInstance("HKDF-SHA256");
// Create an ExtractExpand parameter specification
AlgorithmParameterSpec params =
HKDFParameterSpec.ofExtract()
.addIKM(initialKeyMaterial)
.addSalt(salt).thenExpand(info, 32);
// Derive a 32-byte AES key
SecretKey key = hkdf.deriveKey("AES", params);
// Additional deriveKey calls can be made with the same KDF object
未来,随着Java计划在即将发布的JDK版本中添加更多的KDF算法(如Argon2),预计将会有进一步的增强。
此外,JEPs 496和497引入了基于后量子密码算法(如模块格基密钥封装机制(ML-KEM)和模块格基数字签名算法(ML-DSA))的加密和数字签名机制,这些机制能够抵御量子攻击。
Java 25:JVM核心性能分析
Java Flight Recorder(JFR)已成为分析Java应用程序的参考工具。
通过JEP 518,其设计得到了重新考虑,以减少对应用程序性能的影响,同时提供更精细、更准确的性能分析。
通过JEP 520,新增了收集跟踪和方法计时信息的功能。这使得我们能够收集可靠且精确的方法调用计数数据,更有效地诊断性能问题,并据此优化代码。
Java语言的未来发展方向是什么?
Java 25还包含处于孵化、预览或实验阶段的功能。这些功能尚未稳定,并且/或者在永久集成到JDK之前需要社区反馈。它们让我们得以一窥Java语言和平台的未来发展方向。
以下是Java 25中已存在但尚未最终确定的功能:
- JEP 507:模式、
instanceof和switch中的基本类型(第三版预览版)——旨在为所有模式类型(record、switch、instanceof)提供统一的语法,并增加对基本类型的支持(目前尚无法实现)。 - JEP 502:稳定值(预览版)——该特性允许延迟不可变类属性的初始化,以通过避免在对象初始化过程中进行不必要的计算(例如,一个可能永远不会被使用的静态最终日志记录器)来提高性能,同时确保值在首次赋值后保持不可变。
- JEP 505:结构化并发(第五版预览)——结构化并发通过虚拟线程实现,是一种现代方法,可将任务拆分为子任务,并在不同的线程中并行运行,同时简化生命周期管理,尤其是错误处理。
- JEP 470:加密对象的PEM编码(预览版)——此更新增加了以PEM(隐私增强邮件)格式读写加密对象(密钥、证书等)的能力。PEM是一种广泛用于存储和交换加密数据的标准,而Java之前并未原生支持此格式。
- JEP 489:Vector API(第十次孵化)——这是Vector API的第十次孵化,旨在为并行数据处理提供高效的向量操作,为计算密集型应用带来显著的性能提升。
结论
Java 25 是一个长期支持(LTS)版本,在多个领域带来了众多改进和创新:性能、安全性、代码和API增强、性能分析等。
在这一版本中,OpenJDK继续发展Java语言的核心,同时加快了莱登项目(Project Leyden)工作的整合,以提高应用程序性能,特别是在启动时间和内存消耗方面,这是云原生应用程序的两个关键方面。
Java 25 还通过新功能增强了开发者的体验,这些功能简化了代码,使初学者和来自其他语言的开发者更容易上手。
通过对Java Flight Recorder(JFR)进行多项增强,应用程序性能分析得到了进一步加强,在减少对应用程序开销的同时,提供了更详细、更准确的性能分析。
最后,Java 25 通过引入能够应对量子计算挑战的特性,为未来做好了准备,其中包括创建了一个 KDF(密钥派生函数)API。
原文地址: https://www.sqli.com/int-en/insights-news/blog/java-25
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/3048.html
在这个努力程度如此低下的时代,还轮不到比拼天赋。静下心来,just do it

暂无评论