前言

Java 25 于 2025 年 9 月 16 日正式发布,标志着多项关键语言、虚拟机与安全特性的成熟落地。本文依据官方 JEP 文档,对 Java 25 中已最终确定(Finalized) 的核心特性进行逐项深度解析,涵盖语义细节、使用约束、典型场景与最佳实践,并对预览/实验性特性予以准确标注。


1. JEP 511:模块导入声明(Module Import Declarations)

1.1 功能概述

模块导入声明(import module M;)允许开发者通过单一语句导入一个模块所导出的所有公共顶级类型(类与接口),并自动包含该模块通过 requires transitive 依赖的其他模块所导出的包。

1.2 语义规则

  • 作用范围:适用于任意源文件,无论是否属于显式模块。
  • 导入内容
    • 模块 M 向当前模块(或 unnamed module)导出(exported) 的所有包中的 public 顶级类型;
    • 所有因 M 声明了 requires transitive N 而被当前模块可读(readable) 的模块 N 所导出的包中的 public 顶级类型。
  • 不导入内容
    • 限定导出(qualified exports)给其他模块的包(除非当前模块是目标模块);
    • 未导出的包;
    • unnamed module(即 classpath 上的代码)。

1.3 名称消歧机制

模块导入可能引入同名类型冲突。Java 编译器遵循以下阴影规则(shadowing) 解析名称:

  1. 单类型导入(Single-type import):如 import java.sql.Date; —— 优先级最高;
  2. 按需包导入(Type-import-on-demand):如 import java.util.*;
  3. 模块导入(Module import):如 import module java.base; —— 优先级最低。

示例

import module java.base;   // 导入 java.util.Date
import module java.sql;    // 导入 java.sql.Date
import java.sql.Date;      // 消歧:Date 指向 java.sql.Date

public class Example {
    Date d; // OK: java.sql.Date
}

1.4 典型应用场景

  • 简化标准库使用
    import module java.base; // 等效于 54 个 import pkg.*;
    List<String> list = List.of("a", "b");
    Path p = Path.of("/tmp");
    
  • 聚合模块导入
    // java.se 是聚合模块,transitively requires java.base 等
    import module java.se; // 导入整个 Java SE API(123 个包)
    

1.5 与紧凑源文件的集成

在紧凑源文件中,import module java.base; 自动生效,无需显式声明。

1.6 与 Java 24 的差异

  • Second Preview(JEP 494) 转为 Final
  • java.se 模块现已声明 requires transitive java.base,使 import module java.se 能完整覆盖标准 API。

2. JEP 512:紧凑源文件与实例主方法(Compact Source Files and Instance Main Methods)

2.1 功能组成

该特性包含两个正交但互补的改进:

  1. 紧凑源文件(Compact Source File):允许省略顶层类声明;
  2. 实例主方法(Instance Main Method):允许 main 方法为非 static

二者共同降低初学者与脚本编写者的认知负担。

2.2 紧凑源文件语义

  • 编译器隐式生成一个 final 顶层类(位于 unnamed package),其成员即为源文件中的字段与方法;
  • 该类无显式名称,不可通过 new 实例化;
  • 自动导入 java.base 模块所有导出类型(等效于 import module java.base;);
  • 必须包含一个可启动的 main 方法(见 2.3)。

示例

// Greet.java
String greeting = "Hello";

String buildMessage(String name) {
    return greeting + ", " + name + "!";
}

void main() {
    IO.println(buildMessage("Java 25"));
}

2.3 实例主方法协议

JVM 启动时按以下顺序选择 main 方法:

  1. 若存在 public static void main(String[]),则调用之;
  2. 否则,若存在无参 main() 方法(任意访问修饰符、任意返回类型,但通常为 void),则:
    • 若为 static,直接调用;
    • 若为实例方法,则:
      • 要求类存在非私有无参构造器
      • 实例化对象后调用其 main() 方法。

注意:实例 main 方法不能声明参数

2.4 java.lang.IO

  • 位于 java.lang,自动可用;
  • 提供基础控制台 I/O:
    public static void print(Object obj);
    public static void println(Object obj);
    public static void println();
    public static String readln(String prompt);
    public static String readln();
    
  • 不再自动静态导入:必须写 IO.println(...),避免魔法行为,便于平滑迁移到普通类。

2.5 程序演进路径

紧凑源文件可无缝演进为普通类:

// 紧凑版
void main() {
    var list = List.of("a", "b");
    IO.println(list);
}

// 演进为普通类
import module java.base;
class MyApp {
    void main() {
        var list = List.of("a", "b");
        IO.println(list);
    }
}

2.6 与 Java 24 的差异

  • 名称由 “Simple Source Files” 改为 “Compact Source Files”;
  • IO 类移至 java.lang
  • 移除 IO 静态方法的自动导入;
  • 实现基于 System.in/out,而非 Console

3. JEP 513:灵活的构造函数体(Flexible Constructor Bodies)

3.1 核心变更

允许在显式构造器调用(super(...)this(...)之前编写语句(称为 prologue),但受限于 early construction context 规则。

3.2 执行模型

构造过程分为两个阶段:

  • Prologue(自底向上执行):子类 → 父类;
  • Epilogue(自顶向下执行)Object → 子类。

此模型确保子类字段可在父类构造器执行前初始化,避免因父类调用被覆写方法而访问未初始化字段。

3.3 Early Construction Context 约束

在 prologue 中(含 super(...) 参数列表):

  • 禁止使用 this(显式或隐式)访问实例字段或方法;
  • 禁止使用 super 访问父类成员;
  • 允许无初始值器(no initializer) 的本类字段进行赋值;
  • 允许访问外围类(enclosing class) 的实例(针对内部类)。

合法示例

class Employee extends Person {
    String officeID; // 无初始值器

    Employee(int age, String officeID) {
        if (age < 18 || age > 67)
            throw new IllegalArgumentException("Invalid age");
        this.officeID = officeID; // OK: 初始化本类字段
        super(age);               // 调用父类构造器
    }
}

非法示例

class X {
    int i;
    String s = "hello";
    X() {
        var x = i;        // Error: 隐式 this.i
        s = "goodbye";    // Error: s 已有初始值器
        super();
    }
}

3.4 安全性提升

解决经典问题:父类构造器调用被覆写方法时,子类字段尚未初始化。

修复前

class Person {
    Person(int age) {
        this.age = age;
        show(); // 调用 Employee.show(),但 officeID 为 null
    }
}
class Employee extends Person {
    String officeID;
    Employee(int age, String officeID) {
        super(age);
        this.officeID = officeID;
    }
    void show() { IO.println("Office: " + officeID); } // prints null
}

修复后

class Employee extends Person {
    String officeID;
    Employee(int age, String officeID) {
        this.officeID = officeID; // 先初始化
        super(age);               // 再调用父类
    }
}
// now prints "Office: CAM-FORA"

3.5 与 Java 24 的差异

  • Third Preview(JEP 492) 转为 Final,无变更。

4. JEP 519:紧凑对象头(Compact Object Headers)

4.1 技术背景

HotSpot 中每个 Java 对象包含一个对象头。传统布局在 64 位 JVM 上占用 12 字节(8 字节 Mark Word + 4 字节 Klass Word,对齐至 16 字节)。

紧凑对象头通过压缩 Klass 指针与 Identity Hash Code,将头大小缩减至 8 字节

4.2 性能收益(来自 JEP 519)

  • SPECjbb2015
    • 堆内存减少 22%
    • CPU 时间减少 8%
    • GC 次数减少 15%(G1 与 Parallel GC);
  • 高并行 JSON 解析:运行时间减少 10%

4.3 启用方式

Java 25 起,启用方式简化为:

$ java -XX:+UseCompactObjectHeaders MyApp

不再需要 -XX:+UnlockExperimentalVMOptions

4.4 非目标

  • 不设为默认:未来版本可能默认启用,但 Java 25 中仍需显式开启;
  • 保留扩展性:已为 Project Valhalla 预留 4 位,必要时可进一步压缩。

5. JEP 521:分代 Shenandoah(Generational Shenandoah)

5.1 功能定位

Shenandoah GC 原为不分代设计。JEP 404(JDK 24)引入实验性分代模式,将堆划分为 Young 与 Old 区域,优化短生命周期对象回收。

Java 25 将其转为 Product Feature

5.2 启用方式

$ java -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational MyApp

不再需要 -XX:+UnlockExperimentalVMOptions

5.3 适用场景

  • 应用具有明显对象生命周期分化(大量短命对象);
  • 需要 Shenandoah 的低暂停特性 + 分代回收的吞吐优势。

5.4 非目标

  • 不改变默认模式:Shenandoah 仍默认使用不分代模式。

6. JEP 503:移除 32 位 x86 端口

6.1 变更内容

正式移除对 Linux 32 位 x86 平台的支持。

6.2 原因

  • 社区使用率极低;
  • 维护成本高,阻碍新特性(Loom、FFM、向量 API)在该端口的实现;
  • 主流硬件已全面转向 64 位。

6.3 影响

受影响用户应迁移至 64 位 JVM(x64 或 AArch64)。


7. 预览与实验性特性概览

以下特性在 Java 25 中仍处于预览或实验阶段,需显式启用:

JEP 特性 状态 启用方式
470 PEM 编码的密码学对象 预览 --enable-preview
502 稳定值(Stable Values) 预览 --enable-preview
505 结构化并发 第五次预览 --enable-preview
507 原始类型在模式匹配、instanceof 和 switch 中的使用 第三次预览 --enable-preview
508 向量 API 第十次孵化 默认启用(孵化特性)
509 JFR CPU 时间剖析 实验性 -XX:+UnlockExperimentalVMOptions(仅 Linux)
514 提前编译命令行优化 Leyden 实验 -XX:AOTCacheOutput=...
515 提前编译方法剖析 Leyden 实验 同上
518 JFR 协作采样 实验性 默认启用(HotSpot/JFR)
520 JFR 方法计时与追踪 实验性 默认启用(HotSpot/JFR)

注意:预览特性(Preview)需 --enable-preview;实验性特性(Experimental)通常需 -XX:+UnlockExperimentalVMOptions,但部分 JFR 特性已默认可用。


总结

Java 25 在语言表达力、内存效率与 GC 能力上取得显著进展:

  • 语言层面:通过模块导入、紧凑源文件、灵活构造器,大幅降低入门门槛与样板代码;
  • 虚拟机层面:紧凑对象头与分代 Shenandoah 为内存敏感与低延迟应用提供更强支撑;
  • 工程演进:移除过时端口,聚焦资源于现代架构。

这些特性共同体现了 Java “渐进式现代化” 的核心哲学——在保持向后兼容的前提下,持续提升开发者体验与运行时性能。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐