JDK JDK 学习手册8 → 27 · JDK 专用学习站
DEEPDIVE · LANGUAGE · JEP 12 / 451

预览特性:试用草案,而不是半成品

JDK 21 · 深钻主题 07 / 08 · 预计 40 分钟
基础四步深钻JEP 12JEP 451
一句话预览特性是已经进入 JDK、但尚未永久的语言或 API 改动:必须显式开启,可能在下一个版本调整或移除。JDK 21 同时存在 final、preview、incubator、experimental 四种状态,分清它们是升级不踩坑的前提。
本质:预览不是「提前发布的正式功能」,而是「邀请你试用并提供反馈的草案」。用它可以,但不能假设它跨版本稳定。

学完你要能自己回答

  1. preview、incubator、experimental 三种状态分别是什么意思?
  2. 为什么预览特性编译和运行都要加 --enable-preview?
  3. JDK 21 中哪些特性是预览,哪些已经正式?
  4. 在 Maven/Gradle 项目中试用预览特性要改哪些配置?
  5. 升级 JDK 时,如何快速判断哪些代码可能受预览特性影响?
01 四种状态:正式菜、试吃菜、后厨菜、实验种子02 三条硬规则

四种状态:正式菜、试吃菜、后厨菜、实验种子01

状态含义开启方式稳定性
Final / 正式永久特性,受兼容性承诺保护无需额外参数高
Preview / 预览已进入 JDK,等待反馈,可能调整--enable-preview(编译 + 运行)低,可能跨版本不兼容
Incubator / 孵化独立模块中的试验 API--add-modules jdk.incubator.*低,API 可能大改
Experimental / 实验需要解锁的实验性 VM 选项-XX:+UnlockExperimentalVMOptions低,可能移除
Deprecated / 弃用仍可用,但不建议新代码使用无需参数计划移除

把 JDK 想成一家餐厅:正式菜随时可点;试吃菜(preview)在菜单上,但厨师可能改配方;后厨菜(incubator)还没上菜单;实验种子(experimental)是 VM 层的试验田。

「预览」不等于「测试版 JDK」。JDK 本身是正式版本,只有被标记为 preview 的特性是草案。

三条硬规则02

  1. 编译和运行必须同时开启:javac --enable-preview --release 21,java --enable-preview。只开一边会报错。
  2. 只能用当前 JDK 的预览特性:用 JDK 21 编译的预览 class 文件,不能拿到 JDK 22 上运行;预览特性不承诺跨版本二进制兼容。
  3. 不要在生产代码中依赖预览 API:可以试用、做原型、写实验分支,但不要把预览特性作为稳定交付的基础。
# 编译:必须指定 --release,预览特性与目标版本绑定
javac --enable-preview --release 21 Demo.java

# 运行:必须再次开启
java --enable-preview Demo

# 忘记运行参数时:
# UnsupportedClassVersionError: Preview features are not enabled
01 预览特性在 class 文件里长什么样02 JDK 21 的状态清单03 顺带一提:动态加载 Agent 的警告

预览特性在 class 文件里长什么样01

启用了预览特性的 class 文件,minor version 被写成 65535(0xFFFF),major version 仍是目标 JDK 的版本号(JDK 21 为 65)。JVM 看到 65535 就要求 --enable-preview。

javap -v Demo.class | head -5
# minor version: 65535
# major version: 65
  • 预览特性与 JDK 版本绑定:JDK 21 的预览 class 不能在 JDK 22 的 JVM 上运行。
  • --release 21 不仅设置 class 版本,还限制可用的 API 和语言特性;不写 --release 容易误用当前 JDK 的新 API。
  • JShell 也需要 --enable-preview,例如 jshell --enable-preview。

JDK 21 的状态清单02

特性JEPJDK 21 状态备注
虚拟线程444正式线程模型正式落地
SequencedCollection431正式集合两端 API
Record Patterns440正式record 解构
Pattern Matching for switch441正式模式 switch
分代 ZGC439正式(产品特性)需 -XX:+ZGenerational
String Templates430预览模板表达式
Unnamed Patterns and Variables443预览用 _ 忽略不需要的变量
Unnamed Classes and Instance Main Methods445预览简化入门程序
Scoped Values446预览作用域内不可变上下文
Structured Concurrency453预览结构化任务作用域
Foreign Function & Memory API442预览(第三轮)JDK 22 转正
Vector API448孵化(第六轮)需要 --add-modules jdk.incubator.vector
这张表只描述 JDK 21 的状态。升级到后续版本时,预览特性可能转正、修改或消失,务必查对应版本页与 JEP 状态。

顺带一提:动态加载 Agent 的警告03

JEP 451 在 JDK 21 中让 JVM 在动态加载 Java Agent 时打印警告,为未来默认禁止做准备。它不是预览特性,而是「准备阶段」的弃用策略。

# 看到这类警告时:
WARNING: A {Java,JVM TI} agent has been loaded dynamically ...
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading ...

# 明确需要时:
java -XX:+EnableDynamicAgentLoading -jar app.jar

这类「准备弃用」的特性与 preview 不同:功能仍然默认可用,但未来版本可能改为默认拒绝。升级时应把它列入检查清单。

01 在构建工具中开启预览02 试用策略:隔离、标记、可回退03 可运行示例:未命名类 + 实例 main

在构建工具中开启预览01

Maven

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <release>21</release>
    <compilerArgs>
      <arg>--enable-preview</arg>
    </compilerArgs>
  </configuration>
</plugin>
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <configuration>
    <argLine>--enable-preview</argLine>
  </configuration>
</plugin>

Gradle

tasks.withType(JavaCompile).configureEach {
    options.release.set(21)
    options.compilerArgs += "--enable-preview"
}

tasks.withType(Test).configureEach {
    jvmArgs += "--enable-preview"
}
  • 编译参数和测试运行参数必须成对配置,否则测试会在类加载时失败。
  • IDE 的「语言级别」和「JVM 参数」是两处独立设置,只改一处同样会失败。
  • CI 中固定 JDK 发行版和版本号(例如 21.0.12),不要用浮动的 21 标签。

试用策略:隔离、标记、可回退02

  1. 隔离:把预览特性封装在独立模块或适配层,业务代码依赖稳定接口。
  2. 标记:在代码注释和构建脚本里明确标注预览版本与开关,方便升级时搜索。
  3. 验证:CI 增加一个「预览开启」的 job,以及一个「关闭预览」的基线 job,确保主路径不依赖预览。
  4. 回退:保留不依赖预览的实现分支;升级前先跑预览相关的编译与测试。
// 用运行时版本做显式检查,避免在不支持的 JDK 上误用
int feature = Runtime.version().feature();
if (feature < 21) {
    throw new IllegalStateException("需要 JDK 21+,当前 " + feature);
}

可运行示例:未命名类 + 实例 main03

// HelloPreview.java —— JDK 21 预览特性 JEP 445
void main() {
    System.out.println("Hello from " + Runtime.version());
    System.out.println("preview demo");
}
javac --enable-preview --release 21 HelloPreview.java
java --enable-preview HelloPreview
Hello from 21.0.12+7-LTS
preview demo

去掉 --enable-preview 运行会失败;把 class 文件拿到 JDK 22 上运行也会失败。这两点正是预览特性「与版本绑定」的直接体现。

01 先猜再看02 费曼自检:状态与升级

先猜再看01

先猜再看预览特性会在下一个 JDK 版本自动转正吗?
不会。预览特性需要经过反馈和修订,可能多轮预览后才转正,也可能被修改或移除。是否转正由后续 JEP 决定,不是时间到了自动生效。
先猜再看用 JDK 21 编译的预览 class,能在 JDK 22 上运行吗?
不能。预览 class 的 minor version 为 65535,且与编译它的 JDK 版本绑定;跨版本运行会报错,必须用对应版本重新编译。
先猜再看incubator 模块和 preview 特性是一回事吗?
不是。Incubator 是独立模块(如 jdk.incubator.vector),通过 --add-modules 使用;Preview 是 JDK 核心 API/语言特性,通过 --enable-preview 使用。两者都可能变化,但机制不同。

费曼自检:状态与升级02

01. 为什么预览特性要求编译和运行都开启参数?
编译期需要允许解析预览语法/API,运行期需要 JVM 允许加载 minor version 65535 的类;缺一不可。
02. 如何判断一个 class 文件是否使用了预览特性?
用 javap -v 查看 minor version,65535 表示预览;也可以看构建参数和 JDK 版本。
03. JDK 21 的虚拟线程是预览吗?
不是,虚拟线程在 JDK 21 已正式(JEP 444)。JDK 21 的预览特性包括 String Templates、Scoped Values、Structured Concurrency 等。
04. 生产项目应该如何使用预览特性?
隔离在实验模块/分支,主路径不依赖;固定 JDK 版本;CI 同时验证开启与关闭预览;升级前重新编译并回归。
05. 「准备弃用」与「预览」有什么共同点和区别?
共同点是都提示未来可能变化;区别是预览默认不可用、需要显式开启,准备弃用通常默认仍可用,但未来会改为禁止或移除。
学习进度 记录保存在本机浏览器 · 不上传

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

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