尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Java 17 到 Java 21 升级实战指南:JDK 18-21 新特性、迁移模式与构建配置全解析

Java 17 到 Java 21 升级实战指南:JDK 18-21 新特性、迁移模式与构建配置全解析 Java 17 到 Java 21 升级实战指南JDK 18-21 新特性、迁移模式与构建配置全解析【免费下载链接】awesome-copilotCommunity-contributed instructions, agents, skills, and configurations to help you make the most of GitHub Copilot.项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-copilot本篇技术指南以 awesome-copilot 仓库中的 Java 17 to Java 21 Upgrade Guide 为核心骨架系统梳理从 JDK 17 升级到 JDK 21 过程中需要掌握的语言特性switch 模式匹配、Record 模式、虚拟线程、序列化集合等、API 变更、弃用警告、Maven/Gradle 构建配置与迁移策略。读完本文你将能够独立完成一个 Java 17 项目到 Java 21 的升级评估与落地改造掌握每条升级路径的可复制代码与验证方法。这份指令文件属于 awesome-copilot 仓库instructions/目录下版本升级指令系列的中间一环与 Java 11 to Java 17 Upgrade Guide 和 Java 21 to Java 25 Upgrade Guide 共同构成完整的跨 LTS 版本升级知识链。其安装与使用方式遵循仓库 Custom Instructions 使用文档将*.instructions.md文件放入工作区.github/instructions/目录或将其内容合并进.github/copilot-instructions.mdCopilot 便会自动在 Java 升级类任务中遵循这些约束与最佳实践。JDK 18-21 特性全景一次升级跨越四个版本Java 21 是继 Java 17 之后的又一个长期支持LTS版本而 JDK 18、19、20 作为中间特性版本为 21 的正式落地贡献了大量经过孵化与预览的 JEP。从 17 到 21语言层与 API 层发生了显著变化升级时建议按以下优先级排序标准化特性优先采用switch 模式匹配JEP 441、Record 模式JEP 440、虚拟线程JEP 444、有序集合JEP 431、KEM APIJEP 452这些特性在 21 中已是标准能力可直接用于生产代码预览特性按需启用字符串模板JEP 430、未命名模式与变量JEP 443、作用域值JEP 446仍处于预览阶段需要显式开启--enable-preview基础设施变更顺势清理UTF-8 默认字符集JEP 400、finalize()弃用JEP 421等属于存量代码清理型变更可结合升级一并处理。下文按语言特性 → API 增强 → 弃用与警告 → 构建配置 → 运行时与 GC → 迁移策略的顺序展开与原指令文档的核心脉络保持一致。语言特性从类型检查到模式匹配的范式转变Pattern Matching for switchJEP 441Java 21 标准化JEP 441 将 switch 的模式匹配能力正式标准化使得 switch 不仅可以匹配常量值还可以直接对对象类型进行匹配并解构。这是从 Java 17 升级到 21 时收益最直观的语法改造点。典型的升级模式将传统instanceof链式判断改写为 switch 表达式。以下示例中Java 17 的写法需要依次进行instanceof判断、强转与分支返回Java 21 的写法将类型匹配、绑定变量与返回值统一在 switch 表达式中完成// Old approach (Java 17) public String processObject(Object obj) { if (obj instanceof String) { String s (String) obj; return s.toUpperCase(); } else if (obj instanceof Integer) { Integer i (Integer) obj; return i.toString(); } return unknown; } // New approach (Java 21) public String processObject(Object obj) { return switch (obj) { case String s - s.toUpperCase(); case Integer i - i.toString(); case null - null; default - unknown; }; }注意新写法中显式的case null - null分支Java 17 中 switch 对null会直接抛出NullPointerException而模式匹配的 switch 允许将null作为独立分支处理这是语义层面需要留意的差异。守卫模式guarded patterns当模式匹配需要附加条件时使用when子句。守卫条件满足时命中该分支否则回落到下一个匹配分支switch (obj) { case String s when s.length() 10 - Long string: s; case String s - Short string: s; case Integer i when i 100 - Large number: i; case Integer i - Small number: i; default - Other; }升级时建议让 Copilot 重点扫描存量代码中的两类结构一是长串else if (obj instanceof X)链二是分散在各处的手动强转 分支返回逻辑它们都可以安全地折叠为单个 switch 表达式。Record PatternsJEP 440Java 21 标准化Record 模式允许在模式匹配中直接对 record 的组件进行解构配合 switch 表达式可以写出极具表达力的数据处理代码。以几何形状模型为例public record Point(int x, int y) {} public record ColoredPoint(Point point, Color color) {} // Destructuring in switch public String describe(Object obj) { return switch (obj) { case Point(var x, var y) - Point at ( x , y ); case ColoredPoint(Point(var x, var y), var color) - Colored point at ( x , y ) in color; default - Unknown shape; }; }case Point(var x, var y)中的var由编译器推导组件类型ColoredPoint(Point(var x, var y), var color)则展示了嵌套解构——一次模式匹配同时展开两层 record。复杂模式匹配Record 模式与守卫模式可以组合出非常强大的校验逻辑。下面的示例一次性完成嵌套结构解构 同色校验在 Java 17 中这需要多行嵌套instanceof与手工取值// Nested record patterns switch (shape) { case Rectangle(ColoredPoint(Point(var x1, var y1), var c1), ColoredPoint(Point(var x2, var y2), var c2)) when c1 c2 - Monochrome rectangle; case Rectangle r - Multi-colored rectangle; }从源码语义推断当业务模型中存在大量不可变数据载体对应 Java 16 引入的 record 类型见 Java 11 to Java 17 Upgrade Guide 中 JEP 395 的介绍时Record 模式是处理这类数据最自然的写法应作为升级后代码评审的重点推荐项。Virtual ThreadsJEP 444Java 21 标准化虚拟线程是 Java 21 最受关注的生产力特性之一它让 JVM 能够以极低的内存开销承载海量并发任务特别适合以阻塞 I/O 为主的业务场景。升级路径非常清晰——将线程池 平台线程的模型替换为每任务一个虚拟线程的模型// Old platform thread approach ExecutorService executor Executors.newFixedThreadPool(100); executor.submit(() - { // blocking I/O operation httpClient.send(request); }); // New virtual thread approach try (var executor Executors.newVirtualThreadPerTaskExecutor()) { executor.submit(() - { // blocking I/O operation - now scales to millions httpClient.send(request); }); }两个关键差异值得强调Executors.newVirtualThreadPerTaskExecutor()创建的执行器是AutoCloseable的可以用 try-with-resources 管理生命周期关闭时会等待已提交任务完成平台线程池的线程数量需要根据硬件与任务类型人工调优示例中的 100 是拍脑袋的值而虚拟线程由 JVM 调度器统一管理开发者不再需要猜测池子开多大。结构化并发预览对于存在父子任务关系的场景21 还提供了StructuredTaskScope预览 API将并发任务组织成可管理、可取消的结构// Structured concurrency (Preview) try (var scope new StructuredTaskScope.ShutdownOnFailure()) { FutureString user scope.fork(() - fetchUser(userId)); FutureString order scope.fork(() - fetchOrder(orderId)); scope.join(); // Join all subtasks scope.throwIfFailed(); // Propagate errors return processResults(user.resultNow(), order.resultNow()); }该 API 的核心价值在于任一子任务失败时ShutdownOnFailure会触发其余任务的快速取消避免父任务等一个永远不会返回的子任务这类资源泄漏。需要注意它属于预览特性正式使用需开启--enable-preview并充分测试后续小节会给出 Maven/Gradle 配置。String TemplatesJEP 430Java 21 预览字符串模板为字符串拼接引入了处理器 插值的机制在提升可读性的同时提供了类型安全的插值处理能力。它仍处于预览阶段// Traditional concatenation String message Hello, name ! You have count messages.; // String Templates (Preview) String message STR.Hello, \{name}! You have \{count} messages.; // Safe HTML generation String html HTML.pUser: \{username}/p; // Safe SQL queries PreparedStatement stmt SQL.SELECT * FROM users WHERE id \{userId};STR处理器执行普通字符串插值HTML、SQL等处理器则可以在插值点执行转义与校验如 SQL 处理器对参数化查询的处理这正是安全插值的含义——把转义逻辑从业务代码迁移到模板处理器中。升级评估时若项目仅需简单拼接建议暂缓采用该预览特性若存在高频手写 HTML/SQL 拼接值得为它单独开一个预览特性开关。Sequenced CollectionsJEP 431Java 21 标准化有序集合接口填补了 Java 集合框架无法统一、双向地访问首尾元素的历史空白。新引入的SequencedCollection、SequencedSet、SequencedMap三个接口让List、Deque、LinkedHashSet、TreeSet、LinkedHashMap等类型获得了一致的首尾访问与逆序视图能力// New methods available on Lists, Deques, LinkedHashSet, etc. ListString list List.of(first, middle, last); String first list.getFirst(); // first String last list.getLast(); // last ListString reversed list.reversed(); // [last, middle, first] // Works with any SequencedCollection SequencedSetString set new LinkedHashSet(); set.addFirst(start); set.addLast(end); String firstElement set.getFirst();升级时凡是用list.get(0)、list.get(list.size() - 1)获取首尾元素的存量代码都可替换为getFirst()/getLast()需要逆序遍历时reversed()返回逆序视图而无需创建副本集合。Unnamed Patterns and VariablesJEP 443Java 21 预览未命名模式_用于在模式匹配中显式表示该位置的值我们不关心可以显著提升涉及 record 解构、异常处理等场景的代码可读性// Ignore unused variables switch (ball) { case RedBall(_) - Red ball; // Dont care about size case BlueBall(var size) - Blue ball size size; } // Ignore parts of records switch (point) { case Point(var x, _) - X coordinate: x; // Ignore Y case ColoredPoint(Point(_, var y), _) - Y coordinate: y; } // Exception handling with unnamed variables try { riskyOperation(); } catch (IOException | SQLException _) { // Dont need exception details handleError(); }catch (IOException | SQLException _)的写法同时适用于多异常合并捕获场景替代了原先声明却从不使用的异常变量。该特性与 Record 模式JEP 440配合度极高可作为后者的配套升级项一并评估。Scoped ValuesJEP 446Java 21 预览作用域值是ThreadLocal的现代替代方案尤其适合与虚拟线程配合的场景。它在保证上下文沿调用链传播能力的同时避免了ThreadLocal在线程复用下的值泄漏与高开销问题// Define scoped value private static final ScopedValueString USER_ID ScopedValue.newInstance(); // Set and use scoped value ScopedValue.where(USER_ID, user123) .run(() - { processRequest(); // Can access USER_ID.get() anywhere in call chain }); // In nested method public void processRequest() { String userId USER_ID.get(); // user123 // Process with user context }使用模式是先绑定where再执行run作用域内读取get绑定只在run的调用链内有效run返回后自动解除不存在跨请求泄漏问题。从原指令文档的表述看其更低的开销与更清晰的语义正是针对虚拟线程大量复用载体线程这一场景设计的。API 增强与基础设施变更UTF-8 by DefaultJEP 400Java 18 标准化自 JDK 18 起UTF-8 成为所有平台上的默认字符集此前依赖平台区域设置。这带来两个层面的影响新代码无需再显式指定StandardCharsets.UTF_8存量代码中可以安全删除本意就是 UTF-8的显式指定// Old explicit UTF-8 specification Files.readString(path, StandardCharsets.UTF_8); Files.writeString(path, content, StandardCharsets.UTF_8); // New default behavior (Java 18) Files.readString(path); // Uses UTF-8 by default Files.writeString(path, content); // Uses UTF-8 by default升级注意点该变更虽然简化了代码但也可能暴露此前依赖非 UTF-8 默认字符集的隐患——例如在 Windows 上曾经默认使用 GBK 等区域编码读写文件的代码升级后行为会改变。建议在跨平台测试阶段专门验证读写行为详见测试建议章节。Simple Web ServerJEP 408Java 18 标准化JDK 18 内置了极简的静态文件服务器适合开发调试、测试桩与本地文档预览无需引入任何外部框架// Command line $ jwebserver -p 8080 -d /path/to/files // Programmatic usage HttpServer server HttpServer.create(new InetSocketAddress(8080), 0); server.createContext(/, new SimpleFileHandler(Path.of(/tmp))); server.start();命令行形式直接以jwebserver启动编程形式则复用com.sun.net.httpserver包中增强过的HttpServerAPI。Internet-Address Resolution SPIJEP 418Java 19 标准化该 JEP 为 Java 19 的定制 DNS 解析能力提供了 SPI 扩展点。通过实现InetAddressResolverProvider应用可以在不修改 JVM 的前提下接管地址解析逻辑适用于服务发现与 DNS 相关的测试场景。原指令文档明确指出其在服务发现与测试场景中的价值属于按需采用的能力升级时无需强制改造。Key Encapsulation Mechanism APIJEP 452Java 21 标准化针对后量子密码学post-quantum cryptography的需求JDK 21 引入 KEM密钥封装机制API。KEM 的作用是让通信双方在公钥基础设施下协商出共享密钥示例使用 ML-KEM 算法KeyPairGenerator kpg KeyPairGenerator.getInstance(ML-KEM); KeyPair kp kpg.generateKeyPair(); KEM kem KEM.getInstance(ML-KEM); KEM.Encapsulator encapsulator kem.newEncapsulator(kp.getPublic()); KEM.Encapsulated encapsulated encapsulator.encapsulate();该 API 面向未来抗量子攻击的安全基础设施普通业务应用在本次升级中通常无需改动但了解其存在有助于评估安全架构的演进方向。弃用与警告处理Finalization DeprecationJEP 421Java 18 弃用finalize()自 JDK 9 起被弃用JDK 18 进一步将其标记为 deprecated for removal。升级时应将存量finalize()清理为Cleaner或 try-with-resources// Deprecated finalize approach Override protected void finalize() throws Throwable { cleanup(); } // Modern approach with Cleaner private static final Cleaner CLEANER Cleaner.create(); public MyResource() { cleaner.register(this, new CleanupTask(nativeResource)); } private static class CleanupTask implements Runnable { private final long nativeResource; CleanupTask(long nativeResource) { this.nativeResource nativeResource; } public void run() { cleanup(nativeResource); } }Cleaner方案通过一个静态Cleaner实例注册清理任务CleanupTask持有需要释放的资源句柄。相比finalize()Cleaner的清理时机更可控且不依赖 GC 前的隐式回调。若资源可以通过 try-with-resources 管理应优先采用后者这是最可预期的释放方式。Dynamic Agent LoadingJEP 451Java 21 警告从 JDK 21 起JVM 在动态加载代理如调试器、APM、profiler 在运行时 attach agent时会打印警告信息。处理策略有三条抑制警告临时方案在启动参数中增加-XX:EnableDynamicAgentLoading仅用于兼容存量工具链迁移工具链优先在 JVM 启动时通过-javaagent加载代理而不是运行时动态 attach更新依赖工具推动所依赖的调试、监控工具升级到支持启动时加载的版本。该变更对应用代码本身通常无侵入主要影响的是运维侧启动脚本与监控工具的配置方式。构建配置更新开启预览特性Maven 与 Gradle 双配置使用任何预览特性字符串模板、结构化并发、未命名模式、作用域值时必须同时为编译期与运行期添加--enable-preview。Maven 配置编译插件与测试插件surefire需要分别配置plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId configuration release21/release compilerArgs arg--enable-preview/arg /compilerArgs /configuration /plugin plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-surefire-plugin/artifactId configuration argLine--enable-preview/argLine /configuration /plugin注意release21/release同时约束了编译目标版本避免误用 21 之后的 API。Gradle 配置使用 Java Toolchain 指定 JDK 21并分别给JavaCompile任务与Test任务添加参数java { toolchain { languageVersion JavaLanguageVersion.of(21) } } tasks.withTypeJavaCompile { options.compilerArgs.add(--enable-preview) } tasks.withTypeTest { jvmArgs(--enable-preview) }原指令文档特别强调预览特性仅在确实需要时才启用并在部署生产前于预发环境充分测试文档结尾有明确告诫。如果项目当前不使用任何预览特性则这两段配置都不需要加入避免引入不必要的构建复杂度。虚拟线程的运行时调优参数虚拟线程在 Java 21 中是标准特性无需任何特殊 JVM 标志即可使用。仅当需要排查调度行为或调优吞吐量时才考虑以下系统属性-Djdk.virtualThreadScheduler.parallelismN # Set carrier thread count -Djdk.virtualThreadScheduler.maxPoolSizeN # Set max pool sizejdk.virtualThreadScheduler.parallelism控制载体线程carrier thread的并行度默认与 CPU 核心数相关jdk.virtualThreadScheduler.maxPoolSize载体线程池的最大线程数上限。这两个属性属于调试与深度调优工具生产环境默认值通常已足够不应盲目调整。运行时与 GC 改进Generational ZGCJDK 21 引入了分代式 ZGCJEP 439将 ZGC 的并发回收能力与分代管理结合在保持低延迟的同时降低 GC 整体开销-XX:UseZGC -XX:ZGenerational升级评估时对于延迟敏感且堆内存较大的应用可以分两步验证先在测试环境用上述参数启动并观察分配模式与 GC 行为再通过基准测试对比分代 ZGC 与当前收集器的暂停时间与吞吐差异。需要注意ZGC 的适用性取决于应用的内存分配特征不可一概而论原指令文档在Runtime and GC Improvements一节中强调要监控分配模式与 GC 行为。迁移策略分步升级路线图Step-by-Step Upgrade Process原指令文档给出了五步升级流程每一步都有明确的验收边界更新构建工具确认所用 Maven / Gradle 版本支持 JDK 21Maven 3.9、Gradle 8.5 是常见的最低要求具体以所选版本官方支持矩阵为准语言特性采用优先落地 switch 模式匹配标准特性收益最直接在存在数据解构需求的场景引入 Record 模式对 I/O 密集型应用评估虚拟线程改造预览特性仅针对明确的使用场景按需启用不全局打开测试对并发相关的改动进行重点测试详见测试建议性能使用新的 GC 选项进行基准测试对比。Code Review Checklist在升级后的代码评审中逐项核对以下清单确保改造完整且无遗漏Convert appropriate instanceof chains to switch expressionsUse record patterns for data destructuringReplace ThreadLocal with ScopedValues where appropriateConsider Virtual Threads for high-concurrency scenariosRemove explicit UTF-8 charset specificationsReplace finalize() methods with Cleaner or try-with-resourcesUse SequencedCollection methods for first/last access patternsAdd preview flags only for preview features in use清单中的每一项都对应前文展开的具体 JEP评审时可直接引用各小节的可运行示例作为改造标准。Common Migration Patterns以下三类迁移模式覆盖了升级中最常见的代码改写需求可直接作为改造模板1. Switch 增强instanceof 链 → switch 表达式// From instanceof chains to switch expressions if (obj instanceof String s) return processString(s); else if (obj instanceof Integer i) return processInt(i); // becomes: return switch (obj) { case String s - processString(s); case Integer i - processInt(i); default - processDefault(obj); };2. 虚拟线程采用平台线程池 → 每任务虚拟线程// From platform threads to virtual threads Executors.newFixedThreadPool(200) // becomes: Executors.newVirtualThreadPerTaskExecutor()3. Record 模式使用手动解构 → 模式解构// From manual destructuring to record patterns if (point instanceof Point p) { int x p.x(); int y p.y(); } // becomes: if (point instanceof Point(var x, var y)) { // use x and y directly }值得注意的是第三种模式同样适用于普通instanceof不限于 switch 内部这是 Java 21 中 Record 模式与 Java 16 引入的记录类见 Java 11 to Java 17 Upgrade Guide 中 JEP 395 的迁移模式互相成就的典型场景。迁移风险预演借鉴仓库的 Migration Hazard Catalog仓库 skills/doc-and-modernize/references/migration-hazards.md 提供了一份与具体技术栈无关的迁移风险目录Migration Hazard Catalog其中总结的触发条件 → 风险 → 检测探针 → 计划动作方法论可以直接复用到本次 Java 升级不完整的移除清单H1例如替换ThreadLocal时必须用 grep 扫描全部模块的引用点而非只改自己负责的目录——漏掉的模块会在升级后编译失败隐性依赖H1 的典型风险某模块可能通过传递依赖仍在使用被移除或弃用的 API升级计划应包含对全部清单文件pom.xml/build.gradle的全局扫描步骤清理对象与移除对象的识别文档建议在计划阶段就运行检测探针如搜索finalize()、StandardCharsets.UTF_8等特征串把风险在规划阶段转化为任务项而非留到评审或生产阶段变成返工与事故。该目录的通用方法论与本文的升级清单恰好互补前者提供如何系统性地发现风险后者提供每个风险对应的 Java 21 改造方案。性能考虑升级后的性能收益取决于特性的正确使用方式原指令文档给出以下关键判断标准虚拟线程的优势边界虚拟线程在阻塞 I/O场景下优势巨大可扩展至百万级并发任务但对CPU 密集型任务收益有限甚至可能引入调度开销不应对所有并发场景无差别替换分代 ZGC对大多数应用可降低 GC 开销但需以实际基准测试为准switch 模式匹配通常比等价的instanceof链更高效因为 JVM 可以针对模式序列生成更优化的类型检查指令有序集合方法getFirst()/getLast()对支持的集合类型提供 O(1) 复杂度优于list.get(list.size() - 1)的等值复杂度但可读性更佳作用域值 vs ThreadLocal在虚拟线程场景下作用域值的开销低于ThreadLocal这源于其值传播机制不依赖线程局部存储的写入与读取。测试建议针对升级后的特性使用原指令文档提出五类重点测试项虚拟线程高并发测试虚拟线程应用必须在真实的高并发压力下验证重点关注阻塞 I/O 混合场景下的吞吐与失败率结构化并发预览场景还需验证子任务失败时的快速取消行为模式匹配覆盖率验证 switch 模式匹配覆盖所有预期分支尤其是null分支与守卫条件的边界值如s.length() 10的临界长度GC 对比基准对分代 ZGC 与其他收集器G1 等进行同场景性能对比记录暂停时间、吞吐与内存占用三项指标UTF-8 跨平台验证由于 JEP 400 改变了默认字符集行为需在不同操作系统上验证文件读写的一致性尤其关注此前依赖非 UTF-8 默认字符集的历史代码预览特性生产前测试所有预览特性在进入生产前必须在预发环境充分验证因为预览 API 可能在后续版本中变更签名或行为。总结从 JDK 17 到 JDK 21 的升级不是一次简单的版本号替换而是一次模式匹配范式 轻量并发模型的双重升级switch 模式匹配与 Record 模式重构了类型处理代码的写法虚拟线程改变了高并发应用的资源模型UTF-8 默认化与finalize()弃用则要求对存量代码做一次系统性清理。按构建工具 → 标准特性 → 预览特性 → 测试 → 性能的路线推进配合本文给出的可复制代码与评审清单即可将升级风险控制在可控范围内。若要继续跟进后续版本演进可参考仓库中同系列的 Java 21 to Java 25 Upgrade Guide其中涵盖了原始类型模式、Class-File API替代 ASM等 JDK 22-25 的进一步变更。最终原则始终是原指令文档结尾的告诫仅在确实需要时启用预览特性并在部署生产前于预发环境中充分测试。【免费下载链接】awesome-copilotCommunity-contributed instructions, agents, skills, and configurations to help you make the most of GitHub Copilot.项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-copilot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表