2小时前  Java系列 |   抢沙发  2 
文章评分 0 次,平均分 0.0

Java 25是Java全新的长期支持(LTS)版本。该版本于2025年9月16日发布。它接替了两年前发布的Java 21,并引入了许多改进,特别是在简化语言(为Switch引入模式匹配、记录模式)以及使用虚拟线程进行并发管理方面。

那么,Java 25怎么样?这个版本带来了哪些新功能和改进?

让我们通过这篇关于Java 21以来所引入变更的概览来一探究竟。

Java,一门加速转型的语言

Java 25带来了众多新功能和改进,延续了先前版本的势头。自Java 21以来,几个中间版本(Java 22、23和24)引入了宝贵的功能,特别是在简化语言和优化性能方面,如减少Java应用程序的启动时间。它们还针对未来量子计算的出现,对API进行了改进并增强了安全性,因为量子计算可能会破解当今的加密算法。

自Java 21以来,总共已集成并最终确定了20多项JEP(Java增强提案)。

这些进步证实,Java在保持其简洁性、可移植性和健壮性等核心原则的同时,正继续巩固其作为现代高性能编程语言的地位。

Java语言增强

Java 25对语言进行了重大改进,旨在提高代码的可读性和可维护性,同时简化其语法。以下是自Java 21以来新增的主要特性:

JEP 456:未命名变量与模式

无论是出于编码风格的原因,还是仅仅因为Java的要求,开发人员经常会在某些上下文中声明变量,但最终并未使用它们。

例如,catch块通常以这种形式编写,其中异常参数ex并未实际使用:

String s = ...;
try {
    int i = Integer.parseInt(s);
    ... i ...
}
catch (NumberFormatException ex) {
    System.out.println("Bad number: " + s); // ex is not used
}

在Java 25中,未使用的变量可以用下划线(_)作为变量名进行声明。
例如,之前的catch块可以重写如下:

String s = ...;
try {
    int i = Integer.parseInt(s);
    ... i ...
}
catch (NumberFormatException _) {        // Unnamed variable
    System.out.println("Bad number: " + s);
}

这种语法在方法参数、lambda表达式、模式匹配等中也是有效的。

JEP 467:Markdown文档注释

迄今为止,Java文档注释主要使用HTML编写,这往往使文档的编写和阅读变得更加复杂。随着JEP 467的引入,Java 25现在允许在文档注释中使用Markdown。这极大地简化了文档的创建,使开发人员能够更直观、更轻松地完成这一过程。Markdown支持还使得能够更好地与各种文档工具和开发平台集成。

JEP 511:模块导入声明

Java 25 引入了通过在源文件中使用单个导入模块声明,即可将模块中所有必需的包导入到 Java 源代码中的功能。这简化了依赖管理,并通过减少从同一模块导入多个包所需的行数,提高了代码的可读性。

Java 25之前的示例:

import java.util.Map;                   // or import java.util.*;
import java.util.function.Function;     // or import java.util.function.*;
import java.util.stream.Collectors;     // or import java.util.stream.*;
import java.util.stream.Stream;         // (can be removed)

...

String[] fruits = new String[] { "apple", "berry", "citrus" };
Map<String, String> m =
    Stream.of(fruits)
          .collect(Collectors.toMap(s -> s.toUpperCase().substring(0,1),
                                    Function.identity()));

Java 25示例:

import module java.base;               // Import all exported packages from java.base

...

String[] fruits = new String[] { "apple", "berry", "citrus" };
Map<String, String> m =
    Stream.of(fruits)
          .collect(Collectors.toMap(s -> s.toUpperCase().substring(0,1),
                                   Function.identity()));

JEP 512:紧凑源代码文件和实例主方法

与其他语言相比,Java的语法相当冗长。为了简化其语法,现在可以用更紧凑的形式编写Java代码。这一演变对于Java新手来说尤其有用,因为它使他们能够更容易地熟悉该语言,而不必立即深入学习类、包、模块或可见性修饰符等更复杂的概念,这些概念在学习初期可能会令人困惑。它特别适合以简洁、类似脚本的风格编写Java程序。

这一特性使得以下操作成为可能:

  • 采用紧凑形式的源文件,不声明多余的类 用更简单、更易理解的实例main()方法
  • 替换传统的公共静态void main(String[] args)方法
  • 在java.lang包中包含一个新类,该类提供简化和更适合初学者的I/O方法(而不是经典的System.out.println
  • 当代码以紧凑格式编写时(见第1点),自动导入java.lang包中的标准API

Java 25之前的示例:

class HelloWorld {
    void main() {
        System.out.println("Hello, World!");
    }
}

Java 25示例:

main() {
    IO.println("Hello, World! in compact style");
}

JEP 513:灵活的构造函数体

在之前的LTS版本中,无法在调用父类构造函数(super())之前编写语句。在某些情况下,这可能会导致不必要的调用,尤其是在初始化对象之前需要进行参数验证时。

在Java 25中,该语言现在允许在super()调用之前编写语句,从而在管理对象初始化方面提供了更大的灵活性。

演示此增强的示例代码:

public class PositiveBigInteger extends BigInteger {
  private final long max;

  public PositiveBigInteger(long value, long max) {
      // Code before calling super()
      if (value <= 0)
          throw new IllegalArgumentException("non-positive value");
      this.max = max;

      // Call super() last : Avoids unnecessary calls to the parent constructor if an exception is raised upstream (not possible before)
      super(value);
  }
}

Java 25的性能提升

Java 25在性能提升方面迈出了重要一步,尤其是整合了Leyden项目的先进技术。

Leyden项目的目标是减少Java应用程序的启动时间和内存占用,从而提高其性能,尤其是在云原生和无服务器环境中。迄今为止,实现近乎即刻启动的主要解决方案是使用GraalVM Native Image编译的本机应用程序,这些应用程序可以在几毫秒内启动。然而,这种方法存在几个局限性:编译时间长且成本高,对反射和某些库的支持有限,有时与传统JVM相比吞吐量较低。

在Java 25中,OpenJDK旨在结合JVM的优点(兼容性、动态优化、成熟的生态系统)以及显著更快的启动时间,这得益于引入了提前编译部分代码的预编译缓存机制。

Java 25中的提前编译(AOT)缓存(JEPs 483、514和515)

通过引入提前编译(AOT)缓存显著提高了应用程序的性能。

通过JEPs 483和514引入的AOT缓存显著缩短了Java应用程序的启动时间。该缓存是在应用程序的训练运行期间构建的,它会记录所使用的类和方法,然后为这些类和方法生成优化的AOT缓存。在应用程序启动时,此缓存会被加载到内存中,从而减少即时(Just-In-Time,JIT)编译的开销,并提高整体启动性能。

JEP 515 通过整合 JIT 收集的性能分析数据,进一步增强了 AOT 缓存。这使得缓存更加高效,因为它基于应用程序的实际运行时行为进行了优化。

在拥有大量类和方法的应用程序中,性能提升尤为显著,启动时间可大幅缩短。根据应用程序的不同,启动速度可提升多达50%。

要构建AOT缓存,您需要添加以下选项:

$ java -XX:AOTCacheOutput=app.aot -cp app.jar com.example.App ...

要使用之前构建的AOT缓存运行应用程序,您需要添加以下选项:

$ java -XX:AOTCache=app.aot -cp app.jar com.example.App ...

JEP 519:紧凑对象头改进

对对象头管理进行了优化,以减小其大小。通过优化对象头的结构,Java 25 降低了应用程序的内存占用——这一优势对于在内存中管理大量对象的应用程序尤其宝贵。此外,这种优化还通过减轻垃圾收集器(GC)的压力,有助于整体性能的提升。

为了验证这一特性的稳定性,甲骨文和亚马逊在数百个生产服务中进行了广泛测试。结果显示,在某些情况下,内存使用率降低了多达20%,处理时间减少了多达10%,垃圾收集(GC)周期减少了多达15%。

此选项默认已禁用。若要启用,您需要添加以下JVM选项:-XX:+CompactObjectHeaders

垃圾收集器(GC)

演进 Java提供了多种垃圾收集器(GC)以满足应用程序的不同需求。在Java 25中,对G1、ZGC和Shenandoah收集器进行了改进。

Shenandoah是一种低延迟垃圾收集器(GC),旨在通过在应用程序执行的同时执行大部分收集工作,从而最大限度地减少应用程序暂停。Shenandoah并非Java中的默认垃圾收集器;它更适合具有超大堆内存的应用程序,如大数据工作负载。通过JEP 521,新增了一种分代模式。默认的“非分代”模式仍然可用。要启用Shenandoah的分代模式,请在JVM中添加以下选项:-XX:ShenandoahGCMode=generational

通过JEP 474,ZGC垃圾收集器也增加了分代模式,从而弃用了非分代模式。由于分代模式通常比非分代模式性能更高,因此未来的开发将聚焦于这一方法。

最后,Java 25的默认垃圾收集器G1也通过JEP 423得到了改进。这些改进旨在减少涉及JNI(Java Native Interface,Java本地接口)函数操作时的G1暂停时间。

Java 25:API增强

Java标准库随着Java 25的发布而不断发展,旨在为开发者提供更多功能。

JEP 502:作用域值

作用域值是一种容器,它允许在同一线程内的方法及其调用者之间,以及与子线程之间高效共享不可变数据,而无需依赖方法参数。与变量不同,作用域值(ScopedValue)只需定义一次,即可在明确规定的执行范围内保持可读状态。

作用域值在虚拟线程环境中特别有用,因为与传统ThreadLocal相比,它们提供了更低的内存和性能开销。虚拟线程轻量级且生命周期短,应用程序可以运行数百万个虚拟线程。在此环境中使用ThreadLocal会导致内存使用过度,如果清理不当甚至会导致内存泄漏。作用域值的设计考虑了虚拟线程,但它们也适用于传统线程。

以下示例展示了一个将已连接用户定义为作用域值的API:

public class ScopedValuesExample {
    // Logger
    private static final Logger log = Logger.getLogger(ScopedValuesExample.class.getName());

    // Declaring a ScopedValue
    private static final ScopedValue<String> USERNAME = ScopedValue.newInstance();

    public static void main(String[] args) {

        // Define the scope with a value
        ScopedValue.where(USERNAME, "Alice").run(() -> {
            log.info("In the principal scope");
            methodA();  // Can access USERNAME without parameters

        }); // USERNAME is no longer accessible here

        // Attempt to access outside scope (raises an exception)
        try {
            String user = USERNAME.get(); // Exception !
        } catch (NoSuchElementException e) {
            log.info("USERNAME is not accessible outside the scope");
        }
    }

    private static void methodA() {
        // Direct access to the value from any method within the scope
        String user = USERNAME.get();
        log.info("Method A - User: " + user);

        methodB(); // Calling a deeper method
    }

    private static void methodB() {
        // The value remains accessible even in nested calls
        String user = USERNAME.get();
        log.info("Method B - User: " + user);
    }
}

JEP 485:流收集器

流收集器是对流API的增强。收集器使得为流创建自定义中间操作成为可能。值得注意的是,通过Collector接口已经可以创建自定义终端操作。

以下示例重现了中间操作Gatherers.limit()

Gatherer<String, ?, String> limit() {
  class Counter {
    int counter;
    Counter(int counter) { this.counter = counter; }
  }
  return Gatherer.ofSequential(
      () -> new Counter(0),
      (counter, element, downstream) -> {
        if (counter.counter++ == 3) {
          return false;
        }
        return downstream.push(element);
      }
  );
}

然而,创建自定义中间操作来重现已有功能(如上文中的limit()示例)效率较低。这些自定义操作无法从JIT编译器的所有优化中受益。

JEP 491:在不锁定虚拟线程的情况下同步虚拟线程

是Java 21中引入的主要创新,并且其与Java生态系统的集成已得到进一步优化。此功能旨在实现两个主要目标:

  • 易于采用——使现有的Java库能更轻松地与虚拟线程协同工作,无需进行更改以避免使用同步方法或同步块。
  • 优化诊断——增强诊断工具,以更有效地识别虚拟线程与平台线程绑定的情况,这种情况会阻止虚拟线程被释放。

虚拟线程仍是Java 21的代表性改进,在Java 25中,其与生态系统的集成得到了进一步提升。

JEP 484:Class-File API

通过这一增强功能,现在可以在运行时动态生成Java类。这为高级用例打开了大门,如即时代码生成、动态代理创建以及其他需要灵活性和动态性的场景。

以下是一个使用Class-File API生成Java类的简单示例:

ClassBuilder classBuilder = ...;
classBuilder.withMethod(
    "fooBar",
     MethodTypeDesc.of(CD_void, CD_boolean, CD_int),
     flags,
     methodBuilder -> methodBuilder.withCode(
         codeBuilder -> {
             Label label1 = codeBuilder.newLabel();
             Label label2 = codeBuilder.newLabel();
             codeBuilder.iload(1)
                        .ifeq(label1)
                        .aload(0)
                        .iload(2)
                       .invokevirtual(ClassDesc.of("Foo"), "foo", MethodTypeDesc.of(CD_void, CD_int))
                        .goto_(label2)
                        .labelBinding(label1)
                        .aload(0)
                        .iload(2)
                       .invokevirtual(ClassDesc.of("Foo"), "bar", MethodTypeDesc.of(CD_void, CD_int))
                        .labelBinding(label2);
                        .return_();
         }
     )
);

生成的代码等同于以下Java代码:

void fooBar(boolean z, int x) {
    if (z)
        foo(x);
    else
        bar(x);
}

JEP 454:外部函数与内存API

外部函数与内存(FFM)API的创建现在提供了对JVM外部管理的代码和数据的访问。此API支持安全调用本机代码(C、C++等)以及访问JVM堆栈之外的内存块。

以下代码展示了一个使用FFM API调用C函数strlen的简单示例,该函数用于计算字符串的长度:

public class FFMTest {
  public static void main(String[] args) throws Throwable {
    // 1. Get a lookup object for commonly used libraries
    SymbolLookup stdlib = Linker.nativeLinker().defaultLookup();

    // 2. Get a handle to the "strlen" function in the C standard library
    MethodHandle strlen =
        Linker.nativeLinker()
            .downcallHandle(
               stdlib.find("strlen").orElseThrow(),
               FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));

    // 3. Get a confined memory area (one that we can close explicitly)
    try (Arena offHeap = Arena.ofConfined()) {

      // 4. Convert the Java String to a C string and store it in off-heap memory
      MemorySegment str = offHeap.allocateFrom("Happy Coding!");

      // 5. Invoke the foreign function
      long len = (long) strlen.invoke(str);
      System.out.println("len = " + len);
    }
    // 6. Off-heap memory is deallocated at end of try-with-resources
  }
}

JEP 472:准备限制JNI的使用

Java 25仍然允许通过Java Native Interface (JNI)Foreign Function & Memory (FFM) API调用本机代码。由于FFM API中的本机代码加载受到限制,默认情况下会发出运行时警告。

为了确保JNI和FFM之间的行为一致,在执行本机代码时,使用JNI也会触发一个警告。

使用 JNI 或 FFM API 时显示的消息:

WARNING: A restricted method in java.lang.foreign.Linker has been called

WARNING: java.lang.foreign.Linker::downcallHandle has been called by com.example.Main in an unnamed module

WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module

WARNING: Restricted methods will be blocked in a future release unless native access is enabled

要禁用所有Java代码的警告消息,您可以使用以下选项:

java --enable-native-access=ALL-UNNAMED ...

sun.misc.Unsafe方法弃用 JEP 471和498旨在为移除sun.misc.Unsafe API(2002年引入,计划从Java 26开始完全移除)提供的内存访问方法做好生态系统准备。此API中的方法被视为危险方法,因为它们允许直接访问内存,这可能导致安全和应用程序稳定性问题。

建议将对此API的调用替换为Java 25中引入的新外部函数与内存(FFM)API(见上文JEP 454)。

每当应用程序直接或间接使用这些方法时,都会发出警告。

JEP 458:启动多文件源代码程序

自Java 11起,无需事先编译步骤,即可直接从.java源文件运行程序。Java启动器会在执行前自动在内存中编译程序。

JEP 458 更进一步,它支持简化依赖于额外源文件的Java程序的执行。它允许启动一个使用另一个源文件中定义的类的源文件,同时后者也会在内存中自动编译。源文件的搜索遵循通常的Java目录层次结构,该结构反映了包的结构。

只有主程序实际使用的源文件才会在内存中进行编译。

JEP 486:永久禁用安全管理器

安全管理器很少用于保护服务器端代码,且维护成本高昂。此功能在 Java 17 中已被弃用。

由于安全管理器的采用率较低,且大多数框架和工具已停止对其支持,安全管理器现已被永久禁用,在Java 25中不再可用。

JEP 503:移除32位x86端口支持

在Java 25中已移除对32位x86架构的32位x86端口支持。

与实际使用情况相比,这种架构的维护成本已经变得过高。鉴于目前绝大多数现代系统都运行在64位架构上,继续支持32位已变得越来越无足轻重。

Java为后量子时代做好准备

鉴于量子计算未来的发展——这可能会使当今的加密算法变得脆弱——Java必须开始为未来几年内集成混合、抗量子加密机制奠定基础。

借助JEP 510,Java现在支持RFC 5869中定义的HKDF(基于HMAC的密钥派生函数)算法。该算法使用底层加密哈希函数,从初始密钥和可选盐值中派生出秘密密钥。

使用HKDF-SHA256创建派生密钥的示例:

// Create a KDF object for the specified algorithm
KDF hkdf = KDF.getInstance("HKDF-SHA256");

// Create an ExtractExpand parameter specification
AlgorithmParameterSpec params =
    HKDFParameterSpec.ofExtract()
                    .addIKM(initialKeyMaterial)
                    .addSalt(salt).thenExpand(info, 32);

// Derive a 32-byte AES key
SecretKey key = hkdf.deriveKey("AES", params);

// Additional deriveKey calls can be made with the same KDF object

未来,随着Java计划在即将发布的JDK版本中添加更多的KDF算法(如Argon2),预计将会有进一步的增强。

此外,JEPs 496和497引入了基于后量子密码算法(如模块格基密钥封装机制(ML-KEM)和模块格基数字签名算法(ML-DSA))的加密和数字签名机制,这些机制能够抵御量子攻击。

Java 25:JVM核心性能分析

Java Flight Recorder(JFR)已成为分析Java应用程序的参考工具。

通过JEP 518,其设计得到了重新考虑,以减少对应用程序性能的影响,同时提供更精细、更准确的性能分析。

通过JEP 520,新增了收集跟踪和方法计时信息的功能。这使得我们能够收集可靠且精确的方法调用计数数据,更有效地诊断性能问题,并据此优化代码。

Java语言的未来发展方向是什么?

Java 25还包含处于孵化、预览或实验阶段的功能。这些功能尚未稳定,并且/或者在永久集成到JDK之前需要社区反馈。它们让我们得以一窥Java语言和平台的未来发展方向。

以下是Java 25中已存在但尚未最终确定的功能:

  • JEP 507:模式、instanceofswitch 中的基本类型(第三版预览版)——旨在为所有模式类型(record、switch、instanceof)提供统一的语法,并增加对基本类型的支持(目前尚无法实现)。
  • JEP 502:稳定值(预览版)——该特性允许延迟不可变类属性的初始化,以通过避免在对象初始化过程中进行不必要的计算(例如,一个可能永远不会被使用的静态最终日志记录器)来提高性能,同时确保值在首次赋值后保持不可变。
  • JEP 505:结构化并发(第五版预览)——结构化并发通过虚拟线程实现,是一种现代方法,可将任务拆分为子任务,并在不同的线程中并行运行,同时简化生命周期管理,尤其是错误处理。
  • JEP 470:加密对象的PEM编码(预览版)——此更新增加了以PEM(隐私增强邮件)格式读写加密对象(密钥、证书等)的能力。PEM是一种广泛用于存储和交换加密数据的标准,而Java之前并未原生支持此格式。
  • JEP 489:Vector API(第十次孵化)——这是Vector API的第十次孵化,旨在为并行数据处理提供高效的向量操作,为计算密集型应用带来显著的性能提升。

结论

Java 25 是一个长期支持(LTS)版本,在多个领域带来了众多改进和创新:性能、安全性、代码和API增强、性能分析等。

在这一版本中,OpenJDK继续发展Java语言的核心,同时加快了莱登项目(Project Leyden)工作的整合,以提高应用程序性能,特别是在启动时间和内存消耗方面,这是云原生应用程序的两个关键方面。

Java 25 还通过新功能增强了开发者的体验,这些功能简化了代码,使初学者和来自其他语言的开发者更容易上手。

通过对Java Flight Recorder(JFR)进行多项增强,应用程序性能分析得到了进一步加强,在减少对应用程序开销的同时,提供了更详细、更准确的性能分析。

最后,Java 25 通过引入能够应对量子计算挑战的特性,为未来做好了准备,其中包括创建了一个 KDF(密钥派生函数)API。

原文地址: https://www.sqli.com/int-en/insights-news/blog/java-25

  
 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/3048.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册