从 8 迁移到 11
学完你要能自己回答
- JDK 11 移除了哪些 Java EE / CORBA 模块?Nashorn、CMS、Pack200 是移除还是弃用?
- --release 11 与 -source 8 -target 8 的区别是什么?为什么升级时优先用 --release?
- JDK 11 的 --illegal-access 默认值是什么?它和 JDK 17 的强封装有什么区别?
先对齐版本坐标01
从 8 升到 11,先确认四件事:源码级别、class 文件版本、默认 GC、依赖里是否用了已移除的 Java EE API。JDK 11 的 class 文件版本是 55,默认 GC 是 G1(JDK 9 起)。
| 项目 | JDK 8 | JDK 11 | 迁移影响 |
|---|---|---|---|
| class 文件 | 52 | 55 | 低版本 JVM 无法加载 55 |
| 默认 GC | Parallel | G1 | 停顿特征变化,需重新压测 |
| 模块系统 | 无 | JPMS(9 起) | 模块路径、强封装、--add-opens |
| Java EE / CORBA | 内置 | 移除(JEP 320) | JAXB/JAX-WS 等需外部依赖 |
| Nashorn | 内置 | 弃用 | 15 才移除;11 仍可运行 |
| HttpClient | HttpURLConnection | java.net.http | 新客户端可用,旧 API 仍在 |
| var | 无 | 10 起可用 | 局部变量类型推断 |
java -version
javac -version
openjdk version "11.0.28" 2025-07-15 LTS OpenJDK Runtime Environment Microsoft-11913448 (build 11.0.28+6-LTS) OpenJDK 64-Bit Server VM Microsoft-11913448 (build 11.0.28+6-LTS, mixed mode, sharing) javac 11.0.28
升级顺序建议 8 → 11 → 17,把 11 当作检查点。直接 8 → 17 会把 Java EE 移除、强封装、CMS 移除等问题叠在一起,定位成本更高。
JEP 320:Java EE 与 CORBA 模块被移除02
JDK 11 移除了随 JDK 分发的 Java EE 与 CORBA 模块。很多在 JDK 8 上能编译的代码,在 11 上直接找不到包;这不是模块路径配置问题,而是模块本身不存在。
| 被移除的模块 / API | 典型包 | 替代方案 |
|---|---|---|
java.xml.bind(JAXB) | javax.xml.bind.* | jakarta.xml.bind-api + 实现 |
java.xml.ws(JAX-WS / SAAJ) | javax.xml.ws.*、javax.xml.soap.* | jakarta.xml.ws-api + CXF / Metro |
java.activation | javax.activation.* | jakarta.activation-api + 实现 |
java.xml.ws.annotation | javax.annotation.*(Common Annotations) | jakarta.annotation-api |
java.corba | javax.activity.*、org.omg.* | 没有内置替代;迁移到其他 RPC / 消息方案 |
java.transaction(JTA) | javax.transaction.* | jakarta.transaction-api |
java.se.ee 聚合模块 | — | 不再存在;不要再用 --add-modules java.se.ee |
import javax.xml.bind.DatatypeConverter;
public class JaxbUse {
public static void main(String[] args) {
System.out.println(DatatypeConverter.printHexBinary(new byte[]{1, 2}));
}
}
error: package javax.xml.bind does not exist import javax.xml.bind.DatatypeConverter; ^ error: cannot find symbol symbol: variable DatatypeConverter
try {
Class.forName("javax.xml.bind.JAXBContext");
} catch (ClassNotFoundException e) {
System.out.println("missing=" + e.getMessage());
}
try {
Class.forName("org.omg.CORBA.ORB");
} catch (ClassNotFoundException e) {
System.out.println("corba=missing");
}
missing=javax.xml.bind.JAXBContext corba=missing
注意 java.transaction.xa 仍然存在,javax.transaction.xa.*(XA 接口)没有随 JTA 一起移除。不要因为看到 javax.transaction 就以为所有事务 API 都没了。
javax.annotation.processing 仍在 java.compiler 模块中;被移除的是 Common Annotations 的 javax.annotation.Resource、PostConstruct、PreDestroy 等。包名相同不代表模块相同。弃用不是移除:Nashorn、Pack200、CMS03
JDK 11 的很多「移除」其实是弃用。把 11 当成 17/21 来清理,会误删仍可用的能力;反过来,把弃用当成永久可用,也会在升级到 15/17 时踩坑。
| 能力 | JDK 11 状态 | 移除版本 | 迁移建议 |
|---|---|---|---|
| Nashorn JavaScript | 弃用,仍可运行 | 15(JEP 372) | 改用 GraalJS 等外部引擎 |
| Pack200 / unpack200 | 弃用,工具仍在 | 14(JEP 367) | 改用 jar / jlink |
| CMS GC | 弃用,仍可启动 | 14(JEP 363) | 改用 G1;大堆低延迟评估 ZGC |
Thread.stop/suspend/resume | 弃用,仍可调用 | 后续版本限制 | 改为 interrupt / 协作式取消 |
finalize() | 仍可用,尚未弃用 | 18 弃用 | try-with-resources / Cleaner |
| SecurityManager | 仍可用 | 17 弃用,后续禁用 | 不要在新代码中依赖 |
import javax.script.*;
public class NashornDemo {
public static void main(String[] args) throws Exception {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
System.out.println("engine=" + (engine == null ? "null" : engine.getClass().getName()));
if (engine != null) {
System.out.println("eval=" + engine.eval("1 + 2"));
}
}
}
Warning: Nashorn engine is planned to be removed from a future JDK release engine=jdk.nashorn.api.scripting.NashornScriptEngine eval=3
这段输出证明 Nashorn 在 JDK 11 仍然可用,只是会打印弃用警告。到 JDK 15,getEngineByName("nashorn") 返回 null,而不是启动失败。
java -XX:+UseConcMarkSweepGC -version
java -XX:+PrintGCDetails -version
java -XX:MaxPermSize=256m -version
OpenJDK 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release. [0.007s][warning][gc] -XX:+PrintGCDetails is deprecated. Will use -Xlog:gc* instead. OpenJDK 64-Bit Server VM warning: Ignoring option MaxPermSize; support was removed in 8.0.
反射访问:JDK 11 还是 permit,不是 17 的强封装04
JDK 9 起 JDK 内部 API 被模块封装,但 JDK 9–15 默认仍是宽松模式:第一次非法反射访问会打印警告,然后放行。JDK 11 的默认值是 --illegal-access=permit,不是 deny。
import java.lang.reflect.Field;
public class Reflect {
public static void main(String[] args) throws Exception {
Field f = String.class.getDeclaredField("value");
f.setAccessible(true);
System.out.println("opened=" + (f.get("abc") != null));
}
}
java Reflect
WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by Reflect to field java.lang.String.value WARNING: Please consider reporting this to the maintainers of Reflect WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release opened=true
java --illegal-access=deny Reflect
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make field private final byte[] java.lang.String.value accessible: module java.base does not "opens java.lang" to unnamed module
java --illegal-access=deny --add-opens java.base/java.lang=ALL-UNNAMED Reflect
opened=true
| JDK | 默认行为 | 开关 | 说明 |
|---|---|---|---|
| 9–15 | permit:警告后放行 | --illegal-access=permit|warn|deny|debug | JDK 11 属于这一档 |
| 16 | deny:默认拒绝 | --illegal-access 仍可用 | JEP 396 |
| 17+ | deny,开关移除 | 只能用 --add-opens / --add-exports | JEP 403 |
--add-opens 打开的是深度反射权限,--add-exports 打开的是编译期/运行期的普通访问权限。两者都不是长期方案:优先升级依赖、替换内部 API,把参数限制到最小包和模块。
--illegal-access=deny 跑一遍测试,是成本很低的「17 预演」。如果这时大量报 InaccessibleObjectException,应尽早处理,而不是等到升级 17。--release 11:把 API 边界编译进构建05
--release 11 不仅设置 class 文件版本,还使用 JDK 11 自带的 API 快照检查你只用了 Java 11 标准库。-source 8 -target 8 只约束语法和字节码版本,仍然可以调用 JDK 11 的新 API。
public class OnlyBlank {
public static void main(String[] args) {
System.out.println(" ".isBlank());
}
}
# 能编译,class 版本 52,但运行时可能 NoSuchMethodError
javac -source 8 -target 8 -d st8 OnlyBlank.java
# 编译期就拒绝:isBlank 不在 Java 8 API 中
javac --release 8 -d r8 OnlyBlank.java
-source 8 -target 8: 编译通过(只有 bootstrap class path 警告),class major 52 --release 8: error: cannot find symbol symbol: method isBlank() location: class String
JDK 11 的 javac 支持 --release 6 到 --release 11(6 会提示 obsolete,但仍可编译)。项目里应统一用 --release 11;Maven 配 maven.compiler.release=11,Gradle 配 options.release = 11。
# 依赖与 API 排查
jdeps --jdk-internals app.jar
jdeprscan --release 11 app.jar
# JFR 在 11 已开源,可直接启动录制
java -XX:StartFlightRecording=duration=60s,filename=app.jfr -jar app.jar
# 确认 TLS 1.3(JDK 11 默认支持)
java TlsDemo
- 把所有运行时(本机、CI、容器、K8s)统一到 JDK 11,确认
java -version与javac -version一致。 - 用
--release 11编译,打开-Xlint:removal,先让弃用与移除警告可见。 - 跑
jdeps --jdk-internals,列出仍在使用sun.*/ JDK 内部 API 的依赖。 - 替换 Java EE / CORBA 依赖,把
javax.*迁到jakarta.*或显式第三方实现。 - 清理启动脚本中的 MaxPermSize、CMS、PrintGCDetails 等旧参数;在 11 上它们可能只是警告,到 17 会变成启动失败。
- 用
--illegal-access=deny跑测试,提前暴露反射访问问题。 - 最后再评估模块化:先把应用跑稳在 classpath,再逐个模块化,不要一次性重写。
版本边界BOUNDARY
这张表比 API 本身更值得记。写代码时先确认你的运行时版本。
| 特性 / API | 状态 | 说明 |
|---|---|---|
| Java EE / CORBA 模块 | 11 移除 | JEP 320;JAXB/JAX-WS/activation/Common Annotations/CORBA/JTA 需外部依赖。 |
| java.transaction.xa | 11 保留 | XA 接口仍在;不要把它和 JTA 一起删掉。 |
| Nashorn | 11 弃用 / 15 移除 | 11 仍可运行,会打印弃用警告。 |
| Pack200 | 11 弃用 / 14 移除 | 工具仍在,计划移除。 |
| CMS GC | 9 弃用 / 14 移除 | JDK 11 仍可用;21 上会拒绝启动。 |
| --illegal-access 默认 | 11 permit | 警告后放行;16 默认 deny,17 移除开关。 |
| --release | 9 起提供 | JDK 11 支持 --release 6..11;优先于 -source/-target。 |
| G1 默认 GC | 9 起 | JDK 11 默认 G1,不再是 Parallel。 |
| TLS 1.3 | 11 支持 | JEP 332;默认启用的协议列表包含 TLSv1.3。 |
| ZGC | 11 实验 / 15 正式 | 需要 UnlockExperimentalVMOptions;11 仅 Linux/x64。 |
费曼自检SELF CHECK
先合上页面,用自己的话回答,再展开对照。能讲出因果和反例,才算真正掌握。