Java25-21转正特性梳理
Java25语言特性(已转正)
模块导入说明(Module Import Statements)
Java默认导入了java.lang包,而在Java25,隐式声明的类(紧凑源文件)默认导入 java.base 模块;
// 默认导入,无需手写
import module java.base;
void main() {
IO.println("hello world");
}
歧义导入
使用模块导入声明时,可能会从不同包中导入具有相同简单名称的类。然而,这可能导致编译时错误。以下示例同时使用了 java.awt.List 和 java.util.List:
import module java.desktop;
import module java.base;
public class FruitApp {
public static void main(String args[]) {
Frame f = new Frame();
Label l = new Label("Fruits");
List lst = new List();
}
}
现在ide或者java编译器都会提示错误。
: 错误: 对Label的引用不明确
Label l = new Label("Fruits");
^
java.lang.classfile 中的接口 java.lang.classfile.Label 和 java.awt 中的类 java.awt.Label 都匹配
错误: 对List的引用不明确
List lst = new List();
^
java.util 中的接口 java.util.List 和 java.awt 中的类 java.awt.List 都匹配
改进, 使用类型按需导入声明(import java.awt.*;)来遮蔽模块导入带来的歧义。
import module java.desktop;
import module java.base;
import java.awt.*;
tips:附录写了module导入的具体用法
紧凑源文件和实例 main 方法(Compact Source Files and Instance main Methods)
现在可以一行写出hello world
// 传统写法
package aa;
public class bb {
public static void main(String[] args) {
System.out.println("hello world");
}
}
// Java25写法
void main() {
IO.println("hello world");
}
tips:为了方便Java初学者简单使用封装的打印语句。java一共做了如下的优化。
- 隐式声明的类默认导入了java.base模块
- java.base模块里新增了IO类
- 隐式声明的类取消了main函数必须要声明public static, args参数;取消了class,package定义(java会根据文件名xxx自动生成一个xxx类执行)
灵活的构造函数体(Flexible Constructor Bodies)
public class PositiveBigInteger extends BigInteger {
// 传统写法
public PositiveBigInteger(long value) {
super(Long.toString(value));
if (value <= 0) throw new IllegalArgumentException("non-positive value");
}
// 在Java25支持先对父类构造函数参数进行校验
public PositiveBigInteger(long value) {
if (value <= 0) throw new IllegalArgumentException("non-positive value");
super(Long.toString(value));
}
}
具体细节可以参考
灵活的构造函数体(Flexible Constructor Bodies)
密封类(Sealed Classes)
sealed interface Shape permits Circle, Rectangle, Square {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Square(double side) implements Shape {}
/**
* 显式处理 null
*/
static double area(Shape shape) {
return switch (shape) {
case null -> 0.0d;
case Circle(double radius) -> Math.PI * radius * radius;
case Rectangle(double width, double height) -> width * height;
case Square(double side) -> side * side;
};
}
/**
* 通过when来表达更细粒度的分支
*/
static String classify(Shape shape) {
return switch (shape) {
case null -> "未知的图像";
case Circle(double r) when r > 10 -> "大圆";
case Circle(double r) -> "小圆";
case Rectangle(double w, double h) when w == h -> "正方形(矩形特例)";
case Rectangle(double w, double h) -> "矩形";
case Square(double s) -> "正方形";
};
}
void main() {
IO.println("classify:" + classify(null) + ", area:" + area(null));
Circle circle = new Circle(2.5);
IO.println("classify:" + classify(circle) + ", area:" + area(circle));
}
控制台显示
classify:未知的图像, area:0.0
classify:小圆, area:19.634954084936208
作用域值(Scoped Values)
基础介绍
Java25引入作用域值API,支持方法将不可变数据共享给同一线程内的所有被调用方法,以及关联的子线程。相比线程局部变量(ThreadLocal),作用域值更易于逻辑推导,同时拥有更低的时间和空间开销,尤其在结合虚拟线程(JEP 444)和结构化并发(JEP 505)使用时性能优势明显。
使用示例
import static java.lang.ScopedValue.where;
public static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
private void doWork() {
// 读取
String userId = USER_ID.get();
IO.println("处理了用户:" + userId);
}
void main() {
// 当前线程调用
String userId = "张三";
// 绑定作用域,只在where使用
where(USER_ID, userId)
.run(this::doWork);
// 自动清理,无需remove
// 在虚拟线程中使用
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 提交多个虚拟线程任务,每个任务内部独立绑定
executor.submit(() ->
ScopedValue.where(USER_ID, "Alice").run(() -> {
System.out.println(Thread.currentThread() + " -> " + USER_ID.get());
doWork();
})
);
executor.submit(() ->
ScopedValue.where(USER_ID, "Bob").run(() -> {
System.out.println(Thread.currentThread() + " -> " + USER_ID.get());
doWork();
})
);
} // executor 关闭时自动等待所有任务完成
}
处理了用户:张三
VirtualThread[#43]/runnable@ForkJoinPool-1-worker-1 -> Alice
处理了用户:Alice
VirtualThread[#45]/runnable@ForkJoinPool-1-worker-2 -> Bob
处理了用户:Bob
紧凑对象头(Compact Object Headers)
传统对象头需要存储很多额外信息,比如类型,GC标记,锁状态等元数据。
在64位架构上,对象头从 96 位或 128 位(即 12 或 16 字节)压缩到 64 位(即 8 字节)
注意:该特性在 JDK 25 中需要通过 -XX:+UseCompactObjectHeaders 显式启用
p.s. 不过少了元数据可能会有一些兼容性问题,比如依赖对象头的调试、分析工具
Java 21新特性(已转正)
switch 模式匹配
在 密封类(Sealed Classes) 中有使用,可以参考。
虚拟线程
传统平台线程受限于操作系统线程数量,创建大量线程会导致资源耗尽。虚拟线程的创建成本极低,可以轻松创建数十万个。
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class VirtualThreadDemo {
public static void main(String[] args) {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 100_000).forEach(i ->
executor.submit(() -> {
Thread.sleep(1000); // 模拟 I/O 操作
return i;
})
);
} // executor 关闭时会等待所有任务完成
System.out.println("所有虚拟线程执行完毕");
}
}
附录
模块导入完整示例
module com.example.app {
requires java.sql;
exports com.example.api;
exports com.example.internal to com.example.friend;
uses com.example.spi.PaymentService;
provides com.example.spi.PaymentService
with com.example.impl.AlipayService,
com.example.impl.WechatService;
}
含义:
- 当前模块叫
com.example.app - 它依赖
java.sql模块 com.example.api对所有模块开放com.example.internal只对com.example.friend开放- 它使用
PaymentService服务 - 它提供
PaymentService的两个实现:AlipayService和WechatService
简单总结
| 指令 | 作用 | 语法 |
|---|---|---|
module |
声明模块 | module M { } |
exports |
导出包给所有模块 | exports p; |
exports ... to ... |
只导出给指定模块 | exports p to M1, M2; |
uses |
声明服务消费者 | uses S; |
provides ... with ... |
声明服务提供者 | provides S with P1, P2; |

浙公网安备 33010602011771号