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一共做了如下的优化。

  1. 隐式声明的类默认导入了java.base模块
  2. java.base模块里新增了IO类
  3. 隐式声明的类取消了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;
posted @ 2026-10-15 19:13  Kwanwooo  阅读(6)  评论(0)    收藏  举报