JDK JDK 学习手册8 → 27 · JDK 专用学习站
QUICKREF · LANGUAGE · JEP 512

Compact Source Files:没有 class 外壳的程序

JDK 25 · 速查主题 03 / 06 · 预计 25 分钟
核心25 分钟JEP 512
一句话JDK 25 正式允许单文件程序省略 package 与 class 声明,直接写 import、字段、方法和实例 main。java Hello.java 即可运行;它降低的是入门与小工具的门槛,不是替代正规类结构。

学完你要能自己回答

  1. 紧凑源文件允许哪些顶层成员?隐式类编译后是什么样子?
  2. java Hello.java 与 javac Hello.java 的行为有何不同?类名从哪来?
  3. 紧凑源文件的硬限制是什么?IO.println 为什么不能直接写成 println?

最小程序:没有 package,也没有 class01

JDK 25 的紧凑源文件可以只写一个实例 main 方法。编译器把未包裹的成员放进一个隐式类;本机 javac 以文件名生成类名(规范说生成名实现相关)。用 java Hello.java 启动时,源码在内存中编译并直接运行,不会留下 class 文件。

void main() {
    IO.println("hello, jdk 25");
}
java Hello.java
hello, jdk 25

IO 在 JDK 25 位于 java.lang,因此不需要 import;但它的静态方法不再像 24 预览那样隐式导入,必须写 IO.println,或者显式 import static java.lang.IO.*;。

紧凑源文件在 21–24 是预览(早期叫 simple source files),需要 --enable-preview;25 正式,不再需要预览参数。

实例 main 与顶层成员02

紧凑源文件可以有顶层字段、方法和额外的顶层类。main 可以是实例方法,也可以带 String[] args;启动器会选择合适的主方法。字段和方法默认是隐式类的成员,而不是静态导入。

String greeting = "hello";

void main() {
    IO.println(greeting + " from compact source, twice(21)=" + twice(21));
}

int twice(int x) {
    return x * 2;
}
java CompactHello.java
hello from compact source, twice(21)=42
  • main 可以写 void main()、void main(String[] args),也可以是 static。
  • 顶层字段/方法可以是实例的;隐式类会自动获得无参构造器。
  • 文件里可以再声明普通顶层类,作为辅助类型。
  • 参数通过 String[] args 传入;java ArgsApp.java one two 中 args.length 为 2。
紧凑源文件不是另一种语言方言。它只是省略了外层类声明;需要包、可见性、多个文件或测试时,应该展开成普通类。

编译后是什么样03

javac 仍然可以编译紧凑源文件。本机 javac 生成的隐式类名取自文件名,声明为 final 且包私有;成员也是包私有。规范只保证生成名是实现相关的,源码不应依赖这个类名。下面的 javap 输出来自本机编译结果。

javac --release 25 -d out CompactHello.java
javap -p out/CompactHello.class
Compiled from "CompactHello.java"
final class CompactHello {
  java.lang.String greeting;
  CompactHello();
  void main();
  int twice(int);
}

编译出的类也可以用 java -cp out CompactHello 启动;但紧凑源文件的主要入口仍是源码启动器。源码启动适合单文件工具,编译产物适合继续参与普通 classpath 流程。

隐式类不是公开 API 类型。不要把它当作库的入口,也不要在其他源文件里依赖它的名字。

限制:package、IO 静态导入与单文件04

紧凑源文件不能有 package 声明;这是它与普通源文件最硬的差别。下面的两个错误都在本机 JDK 25 上实际编译得到。

package demo;

void main() {
    IO.println("package?");
}
CompactPackage.java:1: error: compact source file should not have package declaration
package demo;
^
1 error
void main() {
    println("no implicit IO static import");
}
CompactPrintln.java:2: error: cannot find symbol
    println("no implicit IO static import");
    ^
  symbol:   method println(String)
  location: class CompactPrintln
1 error
  • 不能写 package;需要包名时改用普通源文件。
  • IO 的静态方法不再隐式导入;写 IO.println 或 import static java.lang.IO.*;。
  • 一次启动一个源文件;它不是多文件应用的构建方式。
  • 不能声明 module-info;模块导入可以用,但模块声明仍要单独文件。
  • 隐式类是 final、包私有;不适合作为跨包 API。
java Hello.java 不接受 --release 之类的编译选项;需要固定目标版本时,先用 javac --release 25 编译,再运行 class。

什么时候用,什么时候不用05

场景适合紧凑源文件原因
教学、演示、单文件脚本适合没有类外壳,入口清楚
命令行小工具适合一个文件即可分发和运行
库 / 框架 / 多文件应用不适合需要包、可见性、测试与构建
需要固定 --release 的构建不适合源码启动器不接受编译选项
需要公开 API 类型不适合隐式类包私有且 final
  • 先写紧凑源文件验证想法,再展开成普通类;展开时补 package、public class 和显式 main。
  • 不要把业务逻辑堆在隐式类里;超过一个文件就应该进入正常工程结构。
  • 团队规范里明确「单文件工具可以用」,避免和正式源码混在一起。
JEP 512 的目标是降低入门与小工具的门槛,不是让所有 Java 程序都变成脚本。正规项目仍然需要类、包和模块边界。

版本边界BOUNDARY

这张表比 API 本身更值得记。写代码时先确认你的运行时版本。

特性 / API状态说明
紧凑源文件21–24 预览 / 25 正式JEP 512;25 起无需 --enable-preview。
实例 main25 正式支持 void main() / void main(String[] args)。
`java.lang.IO`25 新增println / print / readln。
IO 静态方法隐式导入25 已取消必须写 IO.println 或显式静态导入。
package 声明紧凑源文件不允许编译错误:compact source file should not have package declaration。
隐式类final、包私有本机 javac 以文件名生成类名;规范说生成名实现相关,不要依赖。成员包私有。

费曼自检SELF CHECK

先合上页面,用自己的话回答,再展开对照。能讲出因果和反例,才算真正掌握。

01. 紧凑源文件的 main 可以怎么写?
可以写实例或静态的 void main(),也可以带 String[] args;启动器会选择合适的主方法。隐式类会自动获得无参构造器。
02. java Hello.java 与 javac Hello.java 的关键差别是什么?
前者在内存中编译并直接运行,不产生 class 文件,也不接受 --release 等编译选项;后者生成以文件名命名的 class,可继续用 classpath 运行。
03. 为什么紧凑源文件里不能直接写 println(...)?
JDK 25 起 IO 的静态方法不再隐式导入到紧凑源文件;必须写 IO.println(...),或显式 import static java.lang.IO.*。
04. 紧凑源文件有哪些硬限制?
不能有 package 声明;一次启动一个源文件;不能声明 module-info;隐式类 final 且包私有,不适合作为公开 API。需要这些能力时展开成普通类。
学习进度 记录保存在本机浏览器 · 不上传

JDK 学习手册 · JDK / Java 专用学习站 · 纯静态 · 双击 index.html 即可使用

本站专注 JDK 版本演进与 Java 教学,非 Oracle / OpenJDK 官方站点。版本数据来自 OpenJDK 官方 Features 表;课程示例已在对应本机 JDK 实测。构建日期 2026-11-12。