生产项目一律优先用 --release,因其一次性锁定源语言版本、目标字节码版本及对应JDK完整运行时类库,彻底避免API误用导致的运行时错误;-source/-target仅限老旧构建工具兜底或极特殊非法组合场景,已无实际通用价值。

直接结论:生产项目一律优先用 --release,除非你明确需要旧版 JDK 的类库(比如必须用 JDK 8 的 javax.xml.bind)才退回到 -source/-target 组合。
为什么 --release 能替代 -source 和 -target
--release 不是“语法糖”,它是一次性锁定三件事:源语言版本、目标字节码版本、以及该 JDK 版本对应的完整运行时类库(rt.jar 或 java.base 等模块)。
而 -source 只管语法解析,-target 只管字节码生成——它们都不管你调用了哪个 JDK 的 API。结果就是:用 JDK 17 编译 -source 8 -target 8,代码里写了 Objects.requireNonNullElse(JDK 9 新增),编译能过,但运行时在 JDK 8 上直接 NoSuchMethodError。
--release 会强制编译器只暴露对应版本的 API 表,连 IDE 的自动补全都会受限,这才是真正的兼容性保障。
-
--release 8→ 语法限 Java 8,字节码为 52,类库仅含 JDK 8 的 public API -
--release 17→ 语法限 Java 17,字节码为 61,类库仅含 JDK 17 的 public API - 不支持跨发行版混用,比如
--release 17下无法调用 JDK 21 新增的SequencedCollection
-source 和 -target 还有什么用
它们现在基本只出现在两类场景:
- 构建脚本里临时兜底:某些老 CI 工具或 Ant 脚本没升级到支持
--release(JDK 9+ 才有),只能靠-source/-target配合--boot-class-path手动指定旧类库路径——但这极易出错,且--boot-class-path在 JDK 14+ 已被移除 - 反向兼容极特殊需求:比如你必须在 JDK 17 上编译出能在 JDK 7 上跑的字节码(
-target 7),同时又得用 JDK 17 的新语法(-source 17)——这种组合本身就不合法(source > target),javac 会直接报错
换句话说:-source 和 -target 单独存在已无实际价值;组合使用又受严格约束,还无法防 API 误用。
常见错误现象和排查点
你遇到的 “本地能跑,上线就 NoClassDefFoundError” 或 “IDE 不报错,打包后运行失败”,大概率是用了 -source/-target 却没配类库约束。
- Maven 用户注意:
maven-compiler-plugin的/默认只是设置-source/-target,不是--release。要启用后者,必须显式配置:<configuration><release>17</release></configuration>
- Gradle 用户:
java { toolchain { languageVersion = JavaLanguageVersion.of(17) } }默认走--release(Gradle 6.7+),但老版本需手动加options.release = 17 - IntelliJ IDEA:Project Settings → Project → Project SDK 和 Language Level 是两个独立设置。Language Level 设为 “17 (Preview)” 不等于启用
--release,必须在 Compiler → Java Compiler 里勾选 “Use--releaseoption” 并填版本号
最易被忽略的一点:--release 锁定的是“编译时可见的类库”,不是运行时行为。比如你用 --release 17 编译,却部署到 JDK 21 运行,没问题;但若用了 JDK 21 新增的 JVM 参数(如 +XX:MaxJavaStackTraceDepth),那些参数在 JDK 17 上就无效——兼容性只管 API 层,不管 JVM 内部。











