从 Java 8 直接跳到 Java 25,一次就够了 📅 2026/7/5 13:44:46 摘要Java 8 官方支持已结束社区也不再维护。继续留在 Java 8 等于裸奔——没安全补丁、没性能优化、用不了虚拟线程和 Record。本文不翻译官方文档只讲一个 Java 8 老兵的真实升级经历从 8 直奔 25踩了什么坑、怎么解的、值不值。顺便告诉你为什么别在 17 或 21 浪费时间——跳一次就够管到 2033。背景Java 8 真的不能再用了上个月组里一个新同事问我「这个项目为什么还在用 Java 8」我愣了一下。说实话不是不想升是一想到升级的麻烦就一年拖一年。项目跑了快十年稳得像块石头。谁愿意动它直到有一天安全团队扫出一堆 CVE——全是 Java 8 的洞没人修了。Java 8 是 2014 年发布的。Oracle 的官方支持早就停了社区支持也在今年彻底终止。换句话说你的 Java 8 应用现在连安全补丁都拿不到了。而另一边同事们的新项目用上了虚拟线程、Record、模式匹配 switch——三十行代码干完我三百行的事。每次 Code Review我都在关掉那个手写了两百行 getter 的类。不升不行了。为什么直接跳到 25不是 17 也不是 21决定升级之前我先把几个 LTS 版本捋了一遍版本发布时间支持截止关键卖点Java 82014.03已终止你正在用的Java 172021.09约 2029封印类、Record 成熟Java 212023.09约 2031虚拟线程正式版Java 252025.09至少 2033紧凑对象头、18 项 JEP、最新 LTS算一笔账就清楚了。方案 A8 → 17 → 21 → 25。踩三次坑折腾三轮。每次升级都得处理模块化、废弃 API、第三方依赖兼容性。三次下来光排查问题就能掉半条命。方案 B8 → 25只跳一次。坑集中踩完一步到位。Java 25 是 2025 年 9 月发布的 LTSOracle 承诺至少支持 8 年——从现在算起能管到 2033 年。下一个 LTS 什么时候出还不一定。从生命周期看25 是眼下最「长命」的选择。至于 Java 262026 年 3 月发布非 LTS和还没定版的 Java 27——非 LTS 版本只支持 6 个月根本不值得考虑。结论别走 17 过渡。坑一次踩完就够了。升级实战从 8 到 25 的四道坎跨了 9 个大版本坑是不可避免的。下面这四个是我实打实遇到的。第一坎模块化系统Java 9Java 9 引入了 JPMSJava Platform Module System最直接的影响就是sun.*内部 API 被封了。编译直接报错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如果你的项目或依赖里用了反射访问 JDK 内部类升级后必炸。解法分两步。先用 JDK 自带的jdeps扫一遍jdeps --jdk-internals your-app.jar它会列出所有对sun.*等内部 API 的调用。能改的就改掉——大部分场景 JDK 自己已经提供了替代 API。实在改不了的比如某些老框架深度依赖用--add-opens临时放行java--add-opens java.base/java.langALL-UNNAMED\--add-opens java.base/java.utilALL-UNNAMED\-jaryour-app.jar别把这当永久方案。--add-opens是「先让系统跑起来」的止血措施后面要逐个清理。第二坎被移除的 APIJava 11/17从 Java 8 到 25JDK 删了不少东西。最坑的两个JAXB / JAX-WS 被移除。如果你的项目用了javax.xml.bind.*做 XML 处理升级后直接 ClassNotFoundException。解法Maven 加独立依赖dependencygroupIdjakarta.xml.bind/groupIdartifactIdjakarta.xml.bind-api/artifactIdversion4.0.2/version/dependencyNashorn JS 引擎被干掉。如果代码里调了ScriptEngineManager跑 JavaScript得换 GraalJS。这两个问题不复杂但排查起来很耗时——它们不会在编译期报错只会在运行时崩。第三坎老库不兼容这是我们升级中花时间最多的一块。最大的雷是fastjson 1.x 不支持 Record 序列化。Java 16 引入的 Record 类fastjson 1.x 根本不认识序列化出来是空对象。解法就一条升到 fastjson2。其他几个常见依赖升级Lombok升到 1.18.36否则 Java 25 的 class file 版本不认Byte Buddy / CGLIB各种 AOP 框架底层依赖务必升到最新Hibernate至少 6.x5.x 对 Java 17 的兼容性已经很差了Maven 的maven-enforcer-plugin可以帮你检查依赖的 JDK 兼容性强烈建议先跑一遍。第四坎TLS 和 SecurityManager两个安全相关的变化很容易被忽略。TLS 1.0/1.1 默认禁用Java 17 开始。如果你的应用连的是老版本 MySQL、Redis 或者其他老服务SSL 握手直接失败。日志里会看到SSLHandshakeException。短期解法修改java.security配置文件把jdk.tls.disabledAlgorithms里的 TLSv1 和 TLSv1.1 去掉。长期方案升级数据库和服务端。SecurityManager 永久禁用Java 25。之前几个版本只是标记 deprecatedJava 25 直接不给用了。启动时如果带了-Djava.security.manager参数进程起不来。如果你的应用没用到 SecurityManager——大多数 Java 8 项目都不会用——这事跟你没关系。但如果之前配过必须去掉。踩坑速查表做升级那两周我建了个备忘录。后来发现同事也遇到一样的问题干脆整理成表报错信息原因解法InaccessibleObjectException反射访问内部 APIjdeps --jdk-internals扫描--add-opens放行ClassNotFoundException: javax.xml.bind.*JAXB 被移除加jakarta.xml.bind-api依赖ClassNotFoundException: jdk.nashorn.*Nashorn 被移除换 GraalJSSSLHandshakeExceptionTLS 1.0/1.1 被禁用升级数据库/服务端或临时放行UnsupportedClassVersionErrorclass file 版本不匹配升 Lombok / Byte Buddy / ASMRecord 序列化为空 JSONfastjson 1.x 不支持升到 fastjson2SecurityManager 启动报错Java 25 直接禁了去掉-Djava.security.manager迁移工具——OpenRewrite 一把梭踩完前面那些坑之后我才发现其实有更好的方式。OpenRewrite是一个 Maven 插件能自动帮你改代码——替换废弃 API、升级依赖版本、迁移到新语法。你跑一下它能处理掉大约 60-70% 的机械化改动。配置很简单在pom.xml加一段plugingroupIdorg.openrewrite.maven/groupIdartifactIdrewrite-maven-plugin/artifactIdversion5.43.0/versionconfigurationactiveRecipesrecipeorg.openrewrite.java.migrate.jakarta.JavaxMigrationToJakarta/reciperecipeorg.openrewrite.java.migrate.UpgradeToJava25/recipe/activeRecipes/configuration/plugin然后跑mvn rewrite:run它会自动扫项目、改代码、生成 diff。改完之后git diff看一遍确认没问题再提交。三件 OpenRewrite 帮不了你的事业务逻辑层面的调整比如你针对旧 API 写的 workaround运行时配置--add-opens、java.security这些得手配第三方依赖的内部兼容性问题推荐组合拳OpenRewrite 扫一遍 →jdepsjdeprscan各跑一次 →maven-enforcer-plugin检查依赖 → 剩下的手动改。升级后开哪些新特性按性价比排序费这么大劲升上来不开新特性就亏了。按收益从高到低排1. 紧凑对象头零代码改动性价比最高没有之一。加一个 JVM 参数就生效-XX:UseCompactObjectHeaders对象头从 12 字节压缩到 8 字节。实测下来堆内存降 3-5%CPU 开销最高降 30%。什么都不用改重启就能吃的性能红利。2. 虚拟线程低改动高收益原来用线程池处理并发任务一百个线程就是一百个平台线程内存和上下文切换开销都不小。虚拟线程一个平台线程上能挂成千上万个varexecutorExecutors.newVirtualThreadPerTaskExecutor();executor.submit(()-{// 处理请求该阻塞就阻塞不怕});百万级并发不是梦。Java 24 还修了 synchronized 导致的 pinning 问题到 25 已经相当稳了。3. Record 模式匹配写法爽告别手写 getter/setter/toStringrecordUser(Longid,Stringname,Stringemail){}配合模式匹配 switchvarresultswitch(obj){caseUser(varid,varname,_)-用户: name;caseOrder(varorderId,varamount)-订单: orderId;default-未知类型;};代码量砍一半可读性翻倍。写惯了之后回到 Java 8 会觉得自己在写废代码。4. 其他锦上添花文本块Java 15三引号写 SQL/JSON不用再拼 \n了向量 APIJava 16-25 正式版做 AI 和数据处理SIMD 指令直接上不用 JNI密封类Java 17限制继承关系编译器帮你把关总结升级这事越想越怕。真动手了也就两周。从 Java 8 直奔 25踩一次坑管到 2033。别走 17 过渡——每多一次过渡就多一轮排查纯粹浪费时间。如果你们组还在 Java 8 上我的建议是先拿一个非核心服务试水把上面这些坑趟一遍。跑稳了再把核心服务搬过来。等你用上虚拟线程和 Record再回头写 Java 8 代码会觉得像在用十年前的手机。参考Oracle Java SE Support Roadmap: oracle.comJava 25 LTS 升级指南: xlabs.clubJDK Release Matrix: javaalmanac.io作者唐悦玮 公众号同名从后端出发用 AI 拓展到全栈的工程师。