编程

Java 25 发布在即,新特性一览

8 2025-09-14 00:37:00

1. 概述

Java 25 即将发布!新的长期支持(LTS)版本计划于 2025 年 9 月发布,在 Java 语言、标准库、API 和运行时引入了一套全面的增强功能。

本教程中,我们将探讨截至 2025 年 6 月 Java 25 中引入的所有新功能和更改。让我们通过一个简单的代码示例和详细的解释来理解它们。

2. 语言和编译器特性

Java 25 引入了一套语言和编译器增强功能,旨在使语言更具表现力和简洁性。这些功能增强了开发人员在日常任务和高级模块化场景中的体验。

2.1. 模式中的基元类型(JEP 507 – 第三 Preview)

现在,模式匹配可以处理 switchinstanceof 语句中的基元类型。例如:

static void test(Object obj) {
    if (obj instanceof int i) {
        System.out.println("It's an int: " + i);
    }
}

JEP 507 将基元类型引入 Java 的模式匹配框架,使这些表达式更简单,并减少了样板代码。这个 JEP 是统一整个语言类型模式的更广泛努力的一部分。

2.2. 模块导入声明 (JEP 511 – Preview)

JEP 511 引入了模块导入声明,允许我们通过 import 语句声明它们的依赖关系使用模块,从而提高了模块的可读性。传统上,模块中的依赖关系仅在 module-info.java 描述符中使用 requires 指令声明。。这提高JEP 511 引入了一种使用 Java 文件顶部的 import mofule 语句声明模块依赖关系的方法,类似于传统的 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;      // exports java.util, which includes java.util.Date
import module java.sql;       // exports java.sql, which also includes 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);
    }
}

最后,该变更也允许我们将星号导入转换成使用模块导入:

// These imports could be coalesced:
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 – Final)

灵活的构造函数函数体(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 增强

除了新增 API 的持续工作之外,Java 25 也为一系列现有 API 带来了一系列改进。

3.1. Scoped Value (JEP 506 – Final)

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());
            }));

            // Optional delay to ensure output appears before main exits
            Thread.sleep(200);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

Scoped Value(作用域值)旨在以安全、高性能和不可变的方式在调用链之间传递上下文。它们在虚拟线程和结构化并发方面工作得特别好,通过避免内存泄漏和同步开销,为 ThreadLocal 提供了一种高效的替代方案。

注意:当我们将 Scoped Value(作用域值)与虚拟线程一起使用时,访问作用域值的逻辑必须封装在 ScopedValue.where(…).run(…) 作用域内。将任务提交给作用域内的执行器还不够的。该任务本身必须在作用域内任务以保留其绑定。

3.2. 结构化并发 (JEP 505 – 第五 Preview)

JEP 505 旨在通过将相关线程视为具有适当生命周期管理的单个单元来简化并发性。第五预览通过用一个静态工厂方法替换构造函数和单独的策略方法来改进 API:StructuredTaskScope.open()。这种方法提高了定义自定义连接和错误处理行为的一致性和灵活性。接下来,我们将使用新语法:

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. Stable Value API (JEP 502 – Preview)

Stable Value API (JEP 502) 将类 Optional 的语义扩展到上下文稳定的不可变值:

import java.lang.StableValue;

public class StableExample {
    public static void main(String[] args) {
        // Create a new unset StableValue
        var greeting = StableValue.<String>of();

        String message = greeting.orElseSet(() -> "Hello from StableValue!");
        System.out.println(message);
    }
}

Stable value(稳定值)提供了一个 API,用于在线程或计算之间安全地共享不可变的、上下文稳定的值。它们在涉及缓存、延迟计算或稳定范围内的一致读取的情况下非常方便,并且与结构化并发很好地集成。

3.4. 加密(Cryptographic)对象的 PEM 编码(JEP 470 – Preview)

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 Security API 直接处理 PEM 编码对象,如 X.509 证书和 RSA 密钥,从而消除了对第三方库或手动转换的需要。这提高了与基于 OpenSSL 的系统的互操作性,并简化了安全通信。

3.5. Vector API (JEP 508 – Tenth Incubator)

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

Vector API 启用了数据并行计算,可以在现代中央处理器(CPU)上高效执行。它通过利用 SIMD 指令帮助 Java 代码实现与手工调优的本机代码相当的性能,并且将在孵化器阶段继续发展。

3.6. 密钥派生函数 API (JEP 510 – Final)

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 – Final)

JEP 503 从 OpenJDK 中删除了对传统 32 位 x86 架构的支持。此 JEP 消除了相关性降低的平台的维护开销。64 位 x86 和 ARM64 端口仍然完全受支持。

4.2. JFR CPU-时间分析 (JEP 509 – Experimental)

JEP 509 为 Java飞行记录器(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. 提前编译(AOT)命令行 (JEP 514 – Final)

JEP 514 是 Leyden 项目的一部分,它引入了新的 JVM 命令行标志(-XX:AOTCacheOutput=<file>),这些标志禁用动态功能(如类加载和反射),以评估应用在受限运行时环境中的性能。这些标志帮助我们识别有问题的代码路径,这些路径会阻碍静态图像生成或未来的提前(AOT)编译。

虽然 Java 25 还没有提供内置的 AOT 编译器,但这个 JEP 通过早期实验奠定了基础。它为提供具有可预测启动性能和低内存占用的静态 Java 应用提供长期目标支持。

4.4. 提前编译(AOT)方法分析 (JEP 515 – Final)

JEP 515 引入了方法级分析,它记录了调用哪些方法、调用频率以及其他执行特征。这些数据可以保存和重用,以通知未来的优化,例如提前(AOT)编译。虽然 Java 25 还没有包含 AOT 编译器,但这个 JEP 通过启用配置文件引导的优化,为提高未来版本的启动性能奠定了关键基础。

4.5. JFR 合作采样 (JEP 518 – Final)

JEP 518 允许应用向 Java飞行记录器(JFR)建议安全采样点。协作采样通过将采样与应用定义的安全点对齐来减少开销,提高了准确性,同时最大限度地减少了对性能敏感的代码的干扰。

4.6. 精简对象 Header (JEP 519 – Final)

JEP 519 减小了 64 位架构上的对象头大小。此更改通过在对象头中使用紧凑的布局进行同步和标识数据,从而减少了 Java 对象的内存占用。它特别有利于大栈堆和微服务环境。

4.7. JFR 方法定时和跟踪 (JEP 520 – Final)

JEP 520 通过记录线程上所有方法调用的定时数据(而不仅仅是采样方法)来提高可观察性。这使得能够精确重建方法调用堆栈和时间间隔的持续时间,帮助我们更彻底地分析执行流和并发行为。它通过提供更丰富、确定的跟踪数据而不是概率采样来补充现有的分析功能。

4.8. 代际化 Shenandoah (JEP 521 – Final)

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 发行说明中查阅。

 

相关推荐:

下一篇