Java项目上线前应依据依赖和运行环境选择JDK版本,而非盲目追新:Spring Boot 3.x需JDK 17+,Lombok 1.18.30不兼容JDK 21 record,HttpClient 4.5.x在JDK 17需升级至5.x;优先使用--release保证API、语法与字节码兼容;警惕JDK大版本移除模块(如JDK 11移除JAX-WS)、禁用API(如JDK 21废弃SecurityManager)及隐性行为变更(如stripIndent、虚拟线程类加载器)。

Java项目上线前选错JDK版本,轻则编译失败、运行报错,重则引发生产环境 NoClassDefFoundError 或 IncompatibleClassChangeError。核心原则是:**用项目依赖和目标运行环境倒推JDK版本,而非追新**。
看清楚你用的框架和库支持哪些JDK
Spring Boot 2.7.x 要求最低 JDK 11,但 Spring Boot 3.x 已彻底放弃 JDK 8 支持,强制要求 JDK 17+;Lombok 1.18.30 开始不再为 JDK 21 生成 record 的正确字节码;Apache HttpClient 4.5.x 在 JDK 17 上会因模块系统限制抛出 java.lang.NoClassDefFoundError: javax/net/ssl/SSLContext(需迁移到 5.x)。
- 查清所有直接依赖(
mvn dependency:tree)和间接依赖的Require-Capability或官方文档声明 - 重点关注日志框架(Log4j 2.17+ 才完全修复 JDK 17+ 的 JNDI 问题)、数据库驱动(MySQL Connector/J 8.0.33+ 才默认启用 TLS 1.3)、构建工具(Maven 3.9.0+ 才原生支持 JDK 21)
- 若使用 GraalVM Native Image,JDK 版本必须与
native-image工具链严格匹配,例如 GraalVM CE 22.3 对应 JDK 17,不兼容 JDK 21
区分编译目标(--release)和运行时JDK
用 JDK 17 编译但设 --release 8,生成的 class 文件能在 JDK 8 上运行,但无法使用 var、switch 表达式等语法糖——因为 --release 会禁用对应版本的 API 和字节码特性。而仅靠 -source 8 -target 8 不安全,它不限制 API 使用(比如仍可调用 java.util.Map.of(),该方法 JDK 9 才引入)。
- 推荐优先使用
--release(JDK 9+ 支持),它同时约束语法、API 和字节码版本 - Maven 中配置:
org.apache.maven.plugins maven-compiler-plugin 3.11.0 11 - 若项目需在 JDK 8 环境部署,即使本地用 JDK 17 开发,也必须设
--release 8,否则javac会静默允许调用 JDK 11 的HttpClient
警惕JDK大版本升级带来的隐性断裂
JDK 11 移除了 java.xml.ws(JAX-WS)、java.corba 等 EE 模块;JDK 17 移除了 java.security.acl;JDK 21 将 SecurityManager 标记为废弃并禁用默认策略。这些不是“不推荐”,而是类路径里根本找不到对应类。
本书是全面讲述PHP与MySQL的经典之作,书中不但全面介绍了两种技术的核心特性,还讲解了如何高效地结合这两种技术构建健壮的数据驱动的应用程序。本书涵盖了两种技术新版本中出现的最新特性,书中大量实际的示例和深入的分析均来自于作者在这方面多年的专业经验,可用于解决开发者在实际中所面临的各种挑战。 本书内容全面深入,适合各层次PHP和MySQL开发人员阅读,既是优秀的学习教程,也可用作参考手册。
立即学习“Java免费学习笔记(深入)”;
- 检查代码中是否硬编码了已移除的类(如
javax.xml.ws.Service、com.sun.net.httpserver.HttpServer),它们在 JDK 11+ 会直接ClassNotFoundException - 避免使用
sun.*和com.sun.*包下的类(如sun.misc.BASE64Encoder),它们在 JDK 9+ 模块化后默认不可见,需加--add-exports才能临时绕过,但不可靠 - JDK 17+ 启动时若看到
WARNING: Using incubator modules: jdk.incubator.foreign,说明用了 Foreign Function & Memory API 的预览版,该 API 在 JDK 22 正式发布,但包名从jdk.incubator.foreign变为java.foreign,需改代码
真正麻烦的从来不是“能不能跑”,而是“看起来能跑,但某个边缘场景突然崩”。比如 JDK 17 的 String::stripIndent 在处理含制表符的多行字符串时行为与 JDK 15 不同,而单元测试没覆盖该 case;又比如 JDK 21 的虚拟线程(Thread.ofVirtual())默认不继承上下文类加载器,导致 ServiceLoader 失效。这些细节不会写在版本公告首页,得翻 JEP 文档或实际压测才能暴露。









