1. 概述
Java 25 即将发布!这个新的长期支持版本(LTS)计划于 2025 年 9 月推出,在 Java 语言、标准库、API 和运行时等方面带来全面增强。
本文将详细解析截至 2025 年 6 月 Java 25 的所有新特性。我们将通过简单代码示例和深入解释来理解这些改进。
2. 语言与编译器特性
Java 25 引入了一系列语言和编译器增强,旨在让语言更具表达力和简洁性。这些改进提升了开发者在日常任务和高级模块化场景中的体验。
2.1. 模式匹配支持原始类型(JEP 507 – 第三预览)
现在模式匹配可以在 switch
和 instanceof
语句中处理原始类型。例如:
static void test(Object obj) {
if (obj instanceof int i) {
System.out.println("It's an int: " + i);
}
}
JEP 507 将原始类型引入 Java 的模式匹配框架,使这类表达式更直观,减少样板代码。这是统一语言类型模式更广泛努力的一部分。
2.2. 模块导入声明(JEP 511 – 预览)
JEP 511 引入模块导入声明,允许我们通过 import
语句声明模块依赖,提升模块可读性。传统上,模块依赖只能在 module-info.java
描述符中通过 requires
指令声明。JEP 511 引入了一种在 Java 文件顶部使用 import module
语句声明模块依赖的方式,类似于传统 import
语句。这增强了清晰度,使工具能在开发时更准确地推断依赖。例如:
import module java.base;
//...
public class Main {
public static void main(String[] args) {
Date d = new Date();
System.out.println("Resolved Date: " + d);
}
}
⚠️ 但需注意模糊引用问题。看这个示例:
import module java.base; // 导出 java.util,包含 java.util.Date
import module java.sql; // 导出 java.sql,也包含 java.sql.Date
public class Main {
public static void main(String[] args) {
Date d = Date.valueOf("2025-06-15");
System.out.println("Resolved Date: " + d);
}
}
编译时会报错:
error: reference to Date is ambiguous
Date d = Date.valueOf("2025-06-15");
^
both class java.sql.Date in java.sql and class java.util.Date in java.util match
error: reference to Date is ambiguous
解决方案是添加具体类的导入:
import module java.base;
import module java.sql;
import java.sql.Date;
public class Main {
public static void main(String[] args) {
Date d = Date.valueOf("2025-06-15");
System.out.println("Resolved Date: " + d);
}
}
✅ 此特性还允许将星号导入替换为模块导入:
// 这些导入可以合并:
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.stream.*;
改为:
import module java.xml;
虽然不建议滥用星号导入和模块导入,但这确实使依赖声明更简洁。
2.3. 紧凑源文件(JEP 512)与实例主方法
现在 Java 支持顶级实例 main
方法和无类的紧凑文件。这意味着以下声明是有效的:
void main() {
System.out.println("Hello from Java 25!");
}
JEP 512 基于 Java 21 引入的简化启动器,允许我们编写无需类声明的快速脚本或演示。这些紧凑源文件非常适合教学、脚本编写和快速原型开发,降低了新开发者的入门门槛和学习曲线。
2.4. 灵活的构造函数体(JEP 513 – 正式版)
灵活构造函数体(JEP 513)允许多个构造函数委托给公共初始化体,就这么简单:
class Person {
final int age;
Person(int age) {
this.age = age;
}
}
class Employee extends Person {
final String name;
Employee(String name, int age) {
if (age < 18 || age > 67)
throw new IllegalArgumentException("Age must be between 18 and 67");
super(age); // Java 25 中 super() 不再必须是第一条语句
this.name = name;
}
public static void main(String[] args) {
var emp = new Employee("Alice", 35);
System.out.println("Person age set: " + emp.age);
}
}
在 JEP 513 之前,Java 构造函数要求将 super(...)
或 this(...)
作为第一条语句,这常迫使我们重复验证或初始化逻辑,或将其推入静态辅助方法。JEP 513 允许在构造函数调用前包含代码,使参数验证或共享设置能在一个地方干净地完成,提升了可读性、快速失败行为和对象完整性,同时不破坏 Java 的构造规则。
3. API 增强
Java 25 不仅带来现有 API 的改进,还持续推进仍处于预览、正式和早期阶段的新 API。
3.1. 作用域值(JEP 506 – 正式版)
JEP 506 提供了轻量级、不可变、线程安全的 ThreadLocal
替代方案。专为与虚拟线程协同工作设计:
import java.lang.ScopedValue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ScopedUserExample {
static final ScopedValue<String> USER = ScopedValue.newInstance();
public static void main(String[] args) {
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> ScopedValue.where(USER, "Alice").run(() -> {
System.out.println("Thread: " + Thread.currentThread());
System.out.println("User: " + USER.get());
}));
executor.submit(() -> ScopedValue.where(USER, "Bob").run(() -> {
System.out.println("Thread: " + Thread.currentThread());
System.out.println("User: " + USER.get());
}));
// 可选延迟确保输出在主线程退出前显示
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
作用域值设计用于以安全、高性能、不可变的方式在调用链中传递上下文。它们特别适合虚拟线程和结构化并发,通过避免内存泄漏和同步开销,提供了比 ThreadLocal
更高效的替代方案。
⚠️ 注意:当作用域值与虚拟线程一起使用时,访问作用域值的逻辑必须包装在 ScopedValue.where(...).run(...)
作用域内。仅将任务提交到作用域内的执行器是不够的,任务本身必须在作用域内创建以保留绑定。
3.2. 结构化并发(JEP 505 – 第五预览)
JEP 505 旨在通过将相关线程作为具有适当生命周期管理的单一单元来简化并发。第五预览版通过用单个静态工厂方法 StructuredTaskScope.open()
替换构造函数和独立策略方法来优化 API。这种方法提高了定义自定义连接和错误处理行为的一致性和灵活性。下面使用新语法:
import java.util.concurrent.StructuredTaskScope;
public class StructuredExample {
static String fetchUser() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Alice";
}
static String fetchOrder() {
try {
Thread.sleep(150);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Order#42";
}
public static void main(String[] args) throws Exception {
try (var scope = StructuredTaskScope.<String>open()) {
var userTask = scope.fork(() -> fetchUser());
var orderTask = scope.fork(() -> fetchOrder());
scope.join();
System.out.println(userTask.get() + " - " + orderTask.get());
}
}
}
结构化并发帮助我们管理逻辑相关的多个并发任务。它保证子线程作为整体完成或取消,提高了多线程应用的可靠性和可读性。
3.3. 稳定值 API(JEP 502 – 预览)
稳定值 API(JEP 502)将类似 Optional
的语义扩展到上下文稳定的不可变值:
import java.lang.StableValue;
public class StableExample {
public static void main(String[] args) {
// 创建未设置的稳定值
var greeting = StableValue.<String>of();
String message = greeting.orElseSet(() -> "Hello from StableValue!");
System.out.println(message);
}
}
稳定值提供了一种安全跨线程或计算共享不可变、上下文稳定值的 API。它们在涉及缓存、惰性求值或稳定范围内一致读取的场景中特别有用,并与结构化并发良好集成。
3.4. 加密对象的 PEM 编码(JEP 470 – 预览)
JEP 470 通过标准 API 添加了对 PEM 格式加密密钥和证书的读写支持。新 API 抽象了这些操作,使其变得简单:
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class PEMExample {
public static void main(String[] args) {
String pem = """
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgjDohS0RHP395oJxciVaeks9N
KNY5m9V1IkBBwYsMGyxskrW5sapgi9qlGSYOma9kkko1xlBs17qG8TTg38faxgGJ
sLT2BAmdVFwuWdRtzq6ONn2YPHYj5s5pqx6vU5baz58/STQXNIhn21QoPjXgQCnj
Pp0OxnacWeRSnAIOmQIDAQAB
-----END PUBLIC KEY-----
""";
try {
String base64 = pem.replaceAll("-----.*-----", "").replaceAll("\\s", "");
byte[] keyBytes = Base64.getDecoder().decode(base64);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey key = factory.generatePublic(spec);
System.out.println("Loaded key: " + key.getAlgorithm());
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
}
}
现在我们可以利用 Java 安全 API 直接处理 PEM 编码对象(如 X.509 证书和 RSA 密钥),无需第三方库或手动转换。这提高了与基于 OpenSSL 系统的互操作性,简化了安全通信。
3.5. 向量 API(JEP 508 – 第十孵化器)
JEP 508 提供了一个 API,用于表达向量计算,这些计算能可靠编译为最优向量硬件指令:
import jdk.incubator.vector.*;
public class VectorExample {
public static void main(String[] args) {
float[] left = {1f, 2f, 3f, 4f};
float[] right = {5f, 6f, 7f, 8f};
FloatVector a = FloatVector.fromArray(FloatVector.SPECIES_128, left, 0);
FloatVector b = FloatVector.fromArray(FloatVector.SPECIES_128, right, 0);
FloatVector c = a.add(b);
float[] result = new float[FloatVector.SPECIES_128.length()];
c.intoArray(result, 0);
System.out.println("Vector result: " + java.util.Arrays.toString(result));
}
}
要求:–enable-preview –add-modules jdk.incubator.vector
向量 API 支持可在现代 CPU 上高效执行的数据并行计算。它通过利用 SIMD 指令,使 Java 代码达到与手动调优的本机代码相当的性能,并继续通过孵化器阶段演进。
3.6. 密钥派生函数 API(JEP 510 – 正式版)
Java 25 引入了基于密码的密钥派生函数(如 PBKDF2 和 scrypt)的标准 API:
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
public class KeyDerivationExample {
public static void main(String[] args) throws Exception {
char[] password = "hunter2".toCharArray();
byte[] salt = "somesalt".getBytes();
PBEKeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
SecretKey key = factory.generateSecret(spec);
System.out.println("Derived key format: " + key.getFormat());
}
}
JEP 510 标准化了从用户密码派生加密密钥的广泛使用加密原语的访问,减少了对第三方库的依赖,实现了开箱即用的安全实现。
4. 其他变更
Java 25 的变更还包括配置移除、平台更新和增强。
4.1. 移除 32 位 x86 端口(JEP 503 – 正式版)
JEP 503 从 OpenJDK 中移除了对传统 32 位 x86 架构的支持。此 JEP 消除了对相关性日益下降平台的维护开销。64 位 x86 和 ARM64 端口仍完全支持。
4.2. JFR CPU 时间分析(JEP 509 – 实验性)
JEP 509 为 Java Flight Recorder (JFR) 添加了基于 CPU 时间的分析支持。此特性使我们能记录和分析特定方法或线程花费的 CPU 时间,从而改进性能诊断,特别是在多线程和 I/O 密集型工作负载中。使用新的 JDK.CPULoad
和相关 JFR 事件进行自定义记录:
java
-XX:StartFlightRecording=filename=cpu-time.jfr,duration=10s,settings=profile
--enable-preview
MyApp
然后在 JDK Mission Control 或 VisualVM 中分析 cpu-time.jfr
文件,观察方法和线程的 CPU 使用情况。
4.3. 提前命令行人机工程学(JEP 514 – 正式版)
JEP 514 是 Project Leyden 的一部分,引入了新的 JVM 命令行标志(\-XX:AOTCacheOutput=<file>
),禁用动态特性(如类加载和反射)以评估应用在受限运行时环境中的性能表现。这些标志帮助我们识别阻碍静态映像生成或未来提前 (AOT) 编译的问题代码路径。
虽然 Java 25 尚未提供内置 AOT 编译器,但此 JEP 通过支持早期实验奠定了基础。它支持交付具有可预测启动性能和低内存占用的静态 Java 应用的长期目标。
4.4. 提前方法分析(JEP 515 – 正式版)
JEP 515 引入了方法级分析,记录调用方法及其频率等执行特征。这些数据可保存并重用,为未来优化(如提前编译)提供信息。虽然 Java 25 尚未包含 AOT 编译器,但此 JEP 通过支持分析引导优化,为未来版本改进启动性能奠定了关键基础。
4.5. JFR 协作采样(JEP 518 – 正式版)
JEP 518 允许应用向 Java Flight Recorder 建议安全采样点。协作采样通过将采样与应用定义的安全点对齐来减少开销,在最小化对性能敏感代码干扰的同时提高准确性。
4.6. 紧凑对象头(JEP 519 – 正式版)
JEP 519 减少了 64 位架构上的对象头大小。此更改通过在对象头中使用紧凑布局存储同步和身份数据,缩小了 Java 对象的内存占用。这对大堆和微服务环境特别有益。
4.7. JFR 方法计时与跟踪(JEP 520 – 正式版)
JEP 520 通过记录线程上所有方法调用的计时数据(不仅是采样方法)来增强可观测性。这支持精确重建时间间隔内的方法调用堆栈和持续时间,帮助我们更彻底地分析执行流和并发行为。它通过提供更丰富的确定性跟踪数据(而非概率采样)补充了现有分析功能。
4.8. 分代式 Shenandoah(JEP 521 – 正式版)
JEP 521 为 Shenandoah 垃圾收集器添加了分代支持。分代 GC 通过单独优化新生代收集与长期存活对象,提高了吞吐量和暂停时间性能。它使 Shenandoah 在效率上与 G1 和 ZGC 等收集器保持一致。
5. 开发者须知
如前所述,Java 25 中的许多特性仍处于预览或孵化阶段。要使用这些特性编译和运行代码,必须启用它们。预览代码片段中已展示,但值得更深入理解:
-
–enable-preview
:所有预览特性必需,否则会出现编译错误 -
–add-modules <name>
:孵化模块必需,如之前使用的jdk.incubator.vector
-
–release 25
:编译时推荐使用,以 Java 25 平台为目标
⚠️ 注意:预览和孵化 API 可能在未来版本中更改或移除。应避免在生产中使用它们,或持续关注官方 JDK 文档和发行说明以获取错误修复或问题通知。牢记这一点,要编译和运行使用这些特性的代码,需要:
# 编译时执行
javac --enable-preview --release 25 --add-modules jdk.incubator.vector MyClass.java
# 运行时执行
java --enable-preview --add-modules jdk.incubator.vector MyApp
这样我们就能提示 Java 虚拟机 (JVM) 在编译和运行时允许使用这些特性。
6. 总结
Java 25 持续推动平台向现代化和高效性稳步演进。它优化了许多预览特性,引入了新 API,并在从语言语法到运行时诊断和内存管理的各个层面改进了性能。
此版本还突显了 Java 对现代硬件的承诺,包括分析和附加特性。Java 25 是新的 LTS 版本。它提供了宝贵的优势和新功能,使开发者值得升级应用,以利用自上一个 LTS 版本(Java 21)以来添加的新特性。Java 25 的完整变更列表见 JDK 发行说明。