
来源:juejin.cn/post/7551151758401404962
- 基本类型模式匹配(JEP 507,预览)
- 模块导入声明(JEP 511)
- 更轻量的 Main 方法(JEP 512)
- 更自然的构造函数(JEP 513)
- Record 类的增强(JEP 395/最新增强)
- 结构化并发(JEP 505,预览)
- 作用域值(Scoped Values)(JEP 506)
- 稳定值(Stable Values)(JEP 502,预览)
- 向量 API(JEP 508,孵化)
- 紧凑对象头(JEP 519)
- 分代 Shenandoah GC(JEP 521)
- 提前编译(AOT)优化(JEP 514 & 515)
- JFR 增强(JEP 509, 518, 520)
- 安全性更新(JEP 470, 510)
- 移除 32 位 x86(JEP 503)
- 总结
Java 25 已经正式发布。作为新一轮的长期支持(LTS)版本,它不仅意味着未来几年的稳定性保障,更带来了大量旨在提升开发体验和生产效率的新特性。
这次的更新覆盖了语言语法、并发编程、性能优化、运行时监控以及安全性等多个维度,其核心目标就是让 Java 变得更现代、更高效,同时也更易于上手和使用。
或许你的团队还运行在 Java 8 或 11 上,短期内没有升级计划。但了解新版本的特性和发展趋势仍然大有裨益,它不仅能帮你把握技术演进的脉搏,其中的一些设计思想,比如语法的持续简化和并发模型的革新,也预示着未来几年 Java 生态的发展方向。
1. 基本类型模式匹配(JEP 507,预览)
在过去,当我们在 switch 或 instanceof 中处理基本类型时,代码显得相当繁琐,需要先判断对象类型,再进行手动拆箱和强制转换:
Object obj = 42;
if (obj instanceof Integer) {
int i = (Integer) obj;
System.out.println("这是一个整数:" + i);
}
Java 25 允许我们在模式匹配中直接针对基本类型进行操作:
Object obj = 42;
switch (obj) {
case int i -> System.out.println("整数:" + i);
case double d -> System.out.println("小数:" + d);
default -> System.out.println("其他类型");
}
意义:代码逻辑变得更加直观和安全,彻底告别了冗长且易出错的手动拆箱与转换步骤。
2. 模块导入声明(JEP 511)
对于小型项目、脚本编写或教学场景,逐行导入单个类有时会显得不够高效:
import java.util.List;
import java.util.ArrayList;
Java 25 引入了一种新的导入方式,允许你一次性导入整个模块:
import module java.base;
void main() {
var list = List.of("Java", "c++");
System.out.println(list);
}
意义:在进行 API 探索、编写快速脚本或教学演示时,能够显著减少样板代码,提升效率。
3. 更轻量的 Main 方法(JEP 512)
经典的 Java 程序入口需要遵循固定的模板格式:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
现在,Java 25 允许你以一种更简洁、更像脚本语言的方式定义程序入口:
void main() {
System.out.println("Hello, World!");
}
意义:这极大地降低了 Java 的入门门槛,特别适合快速原型验证、脚本编写以及新手学习。
4. 更自然的构造函数(JEP 513)
在之前的版本中,构造函数必须首先调用 super(),这使得在调用父类构造函数之前进行参数校验变得困难且不直观:
class Man extends Person {
Man() {
super(age); // 这里的age从哪来?逻辑上不好处理
}
}
Java 25 放宽了这一限制,允许在调用 super() 之前执行必要的逻辑,例如输入校验:
class Man extends Person {
Man(int age) {
if (age < 0) age = 18;
super(age);
}
}
意义:构造函数的编写逻辑变得更加自然和安全,能够将校验等前置逻辑放在更合理的位置。
5. Record 类的增强(JEP 395/最新增强)
Record 是 Java 引入的用于声明不可变数据类的语法糖,旨在消灭模板代码。在 Java 25 中,Record 的功能得到了进一步增强。
为什么需要 Record
回忆一下,以前编写一个简单的 DTO(数据传输对象)需要多少代码:
class User {
private final String name;
private final int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String name() { return name; }
public int age() { return age; }
}
使用 Record,一行代码就能达到同等效果,并自动生成构造器、访问器、equals()、hashCode() 和 toString() 方法:
record User(String name, int age) {}
Java 25 的增强
Java 25 允许在 Record 的紧凑构造器中加入校验逻辑,并支持在其中定义自定义方法,让数据类也能承载简单的业务行为。
示例:
record User(String name, int age) {
public User {
if (age < 0) {
throw new IllegalArgumentException("年龄不能小于 0");
}
}
public String greet() {
return "你好,我是 " + name + ",今年 " + age + " 岁";
}
}
public class RecordDemo {
public static void main(String[] args) {
User u = new User("小明", 20);
System.out.println(u.greet());
}
}
意义:新手无需再忍受冗长的模板代码,资深开发者也能以更安全、简洁的方式定义和使用数据模型。
6. 结构化并发(JEP 505,预览)
管理多个并发任务一直是编程中的难点,容易出错且难以维护。Java 25 引入了 结构化并发 的概念,它将一组并发任务视为一个工作单元进行管理。
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var user = scope.fork(() -> fetchUser());
var orders = scope.fork(() -> fetchOrders());
scope.join().throwIfFailed();
System.out.println(user.resultNow() + " " + orders.resultNow());
}
意义:如果任何一个子任务失败,所有其他任务都会被自动取消,资源管理(如线程)也由 try-with-resources 自动处理。这大大提高了并发代码的安全性、可靠性和可读性。
7. 作用域值(Scoped Values)(JEP 506)
这是旨在替代传统 ThreadLocal 的新方案,用于在线程间安全、高效地传递不可变的上下文信息。
static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
void handle(String userId) {
ScopedValue.where(USER_ID, userId).run(() -> {
doHandle();
});
}
void doHandle() {
System.out.println("当前用户:" + USER_ID.get());
}
意义:相比 ThreadLocal,Scoped Values 提供了更清晰的生命周期(与代码块绑定),并且由于不可变性,避免了子线程修改导致父线程数据污染的风险,性能和安全性都更优。
8. 稳定值(Stable Values)(JEP 502,预览)
它提供了一种线程安全的、声明式的懒加载值初始化方式。
StableValue<Config> config = StableValue.of();
Config getConfig() {
return config.orElseSet(this::loadConfig);
}
意义:实现线程安全的懒加载时,我们不再需要编写复杂的“双重检查锁定”(double-checked locking)模式代码,直接用一行声明式的 API 就能搞定,代码简洁且不易出错。
9. 向量 API(JEP 508,孵化)
向量 API 提供了对 SIMD(单指令多数据)指令的底层访问能力,适用于需要高性能数值计算的场景,如科学计算、机器学习和图形处理。
var species = FloatVector.SPECIES_256;
var a = FloatVector.fromArray(species, arr1, 0);
var b = FloatVector.fromArray(species, arr2, 0);
var c = a.add(b);
c.intoArray(result, 0);
意义:在支持的硬件上,可以将循环中的标量运算转换为并行向量运算,从而大幅提升计算密集型任务的性能。
10. 紧凑对象头(JEP 519)
这一 JVM 层面的优化将 64 位系统中的对象头从 128 位减少到 64 位。
意义:直接减少了每个对象实例的内存占用,提高了 CPU 缓存的利用率,从而潜在地提升了整体应用程序性能。对于开发者而言,这是完全透明的优化,无需修改任何代码。
11. 分代 Shenandoah GC(JEP 521)
Shenandoah 垃圾收集器现在支持分代收集。
意义:分代模型使得 Shenandoah 能够更高效地处理大部分朝生夕死的短期对象,从而在保持低停顿时间(低延迟)优势的同时,进一步提高吞吐量,更适合高并发、低延迟要求的应用场景。
12. 提前编译(AOT)优化(JEP 514 & 515)
对 GraalVM 原生镜像(Native Image)和提前编译(AOT)技术的持续优化。
- 启动更快:直接生成原生可执行文件,跳过解释器和即时编译(JIT)的预热阶段。
- 预热更快:对于仍使用 JIT 的场景,优化了预热阶段的性能。
- 适用场景:云原生应用、微服务、函数计算(FaaS)等对快速启动和低内存占用有严格要求的场景将显著受益。
13. JFR 增强(JEP 509, 518, 520)
Java Flight Recorder (JFR) 在生产环境可观测性方面得到了多项增强:
- CPU 时间分析:更精确地度量线程在 CPU 上的实际执行时间。
- 方法执行跟踪:提供更细粒度的方法级别性能分析。
- 更低开销采样:以更低的性能损耗收集运行时数据。
意义:开发者可以以更小的性能代价,获取更丰富、更精准的生产环境性能剖析数据,便于定位性能瓶颈和优化系统。
14. 安全性更新(JEP 470, 510)
- 内置 PEM 编解码:无需依赖第三方库即可处理常见的 PEM 格式密钥和证书文件。
- 标准化 KDF:在标准 API 中内置了对 PBKDF2、Scrypt、Argon2 等密钥派生函数的支持。
意义:简化了安全相关功能的开发,鼓励开发者使用经过标准化和严格审查的官方 API 来实现加密逻辑,从而编写出更安全的应用程序。
15. 移除 32 位 x86(JEP 503)
官方停止对 32 位 x86 架构的支持。
意义:这标志着 Java 全面转向现代 64 位硬件生态。专注于 64 位架构可以简化维护工作,集中精力进行性能优化和新特性开发,使运行时环境更高效、更干净。
总结
纵观 Java 25 的各项更新,我们可以清晰地看到其发展的几个核心方向:
- 更简洁的语法:通过基本类型模式匹配、更轻量的入口、灵活的构造器以及功能增强的 Record,持续降低编码复杂度和认知负担。
- 更安全的并发模型:结构化并发、作用域值和稳定值等特性,旨在解决传统并发编程中的常见陷阱,让编写正确且高效的并发代码变得更容易。
- 更强大的运行时性能:向量 API、紧凑对象头、分代 Shenandoah GC 以及 AOT 优化,从多个层面提升 Java 应用的执行效率和资源利用率。
- 更卓越的可观测性:JFR 的持续增强为生产环境的问题诊断和性能调优提供了更强大的工具支持。
对于初学者而言,这些改进让 Java 语言显得更加友好和现代化;对于经验丰富的开发者,则意味着更高的开发效率、更强的性能表现和更好的系统安全性。无论你所在的团队何时计划升级,了解这些特性都将有助于你更好地规划技术栈的未来。