Java JDK21 新特性详解

JDK21 是 Oracle 发布的继 JDK8、JDK11、JDK17 之后的第四个长期支持版(LTS),于 2023 年 9 月 19 日正式发布,共包含 15 个新特性,涵盖语言增强、性能优化、API 扩展等多个维度,极大提升了开发效率、程序性能和代码安全性。

提示:本文所有代码均基于 JDK21 环境编写,运行前请确保已安装 JDK21 并配置好环境变量;涉及预览特性的代码,需在编译和运行时添加 --enable-preview 参数(具体用法见对应特性说明)。

一、核心正式特性

1. 虚拟线程(Virtual Threads,JEP 444)—— 轻量级高并发利器

1.1 特性说明

虚拟线程是 JDK21 最受关注的核心特性,它是由 JVM 管理的轻量级线程,而非操作系统内核线程,彻底解决了传统平台线程(Platform Thread)资源占用高、上下文切换开销大的问题。虚拟线程的内存占用极低(每个仅需几 KB),可轻松创建数百万个并发任务,尤其适合 I/O 密集型场景(如接口调用、数据库操作、文件读写等),能极大提升程序吞吐量,且无需修改现有线程相关代码,上手成本极低。

核心优势:

  • 轻量级:内存占用远低于平台线程,单个 JVM 可创建数百万个虚拟线程。
  • 低开销:上下文切换由 JVM 管理,无需操作系统内核参与,开销极小。
  • 易迁移:现有基于 RunnableCallable 的代码可直接迁移,无需修改业务逻辑。
  • 结构化并发:配合 Executors.newVirtualThreadPerTaskExecutor(),简化并发任务管理。
  • 调度优化:虚拟线程阻塞时会自动切换到其他正在运行的线程,充分利用 CPU 时间,避免资源闲置。
  • 无规模限制:不受操作系统对线程数量的限制,理论上支持无限量创建(受限于内存)。
1.2 代码样例(4种创建方式,推荐第三种)
importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.ThreadFactory;importjava.util.concurrent.TimeUnit;/** * 虚拟线程 4种创建方式演示 */publicstaticvoidmain(String[] args)throwsInterruptedException{// 方式1:通过 Thread.startVirtualThread() 直接创建(最简单)Thread.startVirtualThread(()->{System.out.println("方式1:虚拟线程运行中,线程类型:"+Thread.currentThread().getClass().getSimpleName());try{// 模拟I/O操作(睡眠100ms)TimeUnit.MILLISECONDS.sleep(100);}catch(InterruptedException e){thrownewRuntimeException(e);}System.out.println("方式1:虚拟线程执行完毕");});// 方式2:通过 Thread.Builder 构建(可设置线程名称,移除不支持的优先级)Thread virtualThread =Thread.ofVirtual().name("virtual-thread-2")// 设置线程名称(虚拟线程支持设置名称).unstarted(()->{System.out.println("方式2:虚拟线程运行中,线程名称:"+Thread.currentThread().getName());try{TimeUnit.MILLISECONDS.sleep(150);}catch(InterruptedException e){thrownewRuntimeException(e);}System.out.println("方式2:虚拟线程执行完毕");}); virtualThread.start();// 启动虚拟线程// 方式3:通过线程池创建(推荐,适合批量管理并发任务)// newVirtualThreadPerTaskExecutor():每个任务对应一个虚拟线程try(ExecutorService executor =Executors.newVirtualThreadPerTaskExecutor()){for(int i =0; i <5; i++){int taskId = i; executor.submit(()->{System.out.println("方式3:虚拟线程任务"+ taskId +"运行中,线程ID:"+Thread.currentThread().threadId());try{TimeUnit.MILLISECONDS.sleep(200);}catch(InterruptedException e){thrownewRuntimeException(e);}System.out.println("方式3:虚拟线程任务"+ taskId +"执行完毕");});}}// try-with-resources 自动关闭线程池,无需手动shutdown// 方式4:通过 ThreadFactory 创建(灵活定制线程属性)// 替换 namePrefix 为 name(prefix, start),移除无效的 daemon 方法ThreadFactory factory =Thread.ofVirtual().name("custom-virtual-thread-",0)// 名称前缀 + 起始数字(0开始).factory();Thread customThread = factory.newThread(()->{System.out.println("方式4:自定义虚拟线程运行中,线程名称:"+Thread.currentThread().getName());try{TimeUnit.MILLISECONDS.sleep(250);}catch(InterruptedException e){thrownewRuntimeException(e);}System.out.println("方式4:自定义虚拟线程执行完毕");}); customThread.start();// 等待所有虚拟线程执行完毕(主线程阻塞,避免程序提前退出)TimeUnit.SECONDS.sleep(1);System.out.println("所有虚拟线程执行完毕,主线程退出");}
1.3 运行结果(关键信息)
方式1:虚拟线程运行中,线程类型:VirtualThread 方式2:虚拟线程运行中,线程名称:virtual-thread-2 方式3:虚拟线程任务3运行中,线程ID:40 方式3:虚拟线程任务2运行中,线程ID:39 方式3:虚拟线程任务4运行中,线程ID:41 方式3:虚拟线程任务0运行中,线程ID:37 方式3:虚拟线程任务1运行中,线程ID:38 方式1:虚拟线程执行完毕 方式2:虚拟线程执行完毕 方式3:虚拟线程任务0执行完毕 方式3:虚拟线程任务2执行完毕 方式3:虚拟线程任务4执行完毕 方式3:虚拟线程任务3执行完毕 方式3:虚拟线程任务1执行完毕 方式4:自定义虚拟线程运行中,线程名称:custom-virtual-thread-0 方式4:自定义虚拟线程执行完毕 所有虚拟线程执行完毕,主线程退出 
1.4 注意事项
  • 虚拟线程适合 I/O 密集型场景,不适合 CPU 密集型场景(CPU 密集型任务建议使用平台线程,避免 JVM 调度开销)。
  • 虚拟线程不能调用 Thread.setDaemon()(守护线程)、Thread.suspend()Thread.resume() 方法,调用会抛出 UnsupportedOperationException
  • 通过 Executors.newVirtualThreadPerTaskExecutor() 创建的线程池,无需手动调用 shutdown(),try-with-resources 会自动关闭。
  • 虚拟线程的优先级设置仅对 JVM 调度有效,不影响操作系统内核线程的优先级。
  • 虚拟线程与平台线程完全兼容,可通过 Thread.isVirtual() 方法判断线程类型。

2. 模式匹配 for switch(Pattern Matching for switch,JEP 441)—— 简化分支判断

2.1 特性说明

JDK21 正式定稿了 switch 模式匹配功能,该特性经过 4 轮预览迭代(JDK17-JDK20),最终成为正式特性。它扩展了 switch 的能力,允许 case 标签使用模式(而非仅常量),支持类型匹配、null 匹配、记录模式、守卫模式等,彻底解决了传统 switch 只能匹配常量、类型判断繁琐、空值处理麻烦的问题,让分支逻辑更简洁、更安全、更易维护。

核心增强点:

  • 支持类型模式:case 可直接匹配指定类型,无需手动强转。
  • 支持 null 匹配:专门的 case null,无需在 switch 外部判断空值。
  • 支持 guarded case(条件守卫):通过 when 子句添加额外判断条件(也支持 && 语法)。
  • 支持穷尽性检查:结合密封类使用时,编译器会检查是否覆盖所有可能的情况,避免遗漏。
  • 改进类型推断:编译器能更好地推断 switch 表达式的返回类型,减少显式声明。
2.2 代码样例(5种常见场景)
importjava.util.Objects;/** * switch 模式匹配 5种常见场景演示 * 要求:Java 21+ 版本运行(所有特性均为正式特性) */publicclassSwitchPatternMatchingDemo{publicstaticvoidmain(String[] args){// 场景1:类型模式匹配(替代 instanceof + 强转)Object obj1 ="JDK21";String typeResult =switch(obj1){caseString s ->"字符串类型,长度:"+ s.length();caseInteger i ->"整数类型,值:"+ i;caseDouble d ->"浮点数类型,值:"+ d;default->"未知类型";};System.out.println("场景1结果:"+ typeResult);// 场景2:null 匹配(无需外部判断空值)String str =null;System.out.print("场景2结果:");switch(str){casenull->System.out.println("字符串为null");case"JDK"->System.out.println("字符串是JDK");default->System.out.println("字符串:"+ str);}// 场景3:条件守卫(统一使用 when 子句,修复 && 语法错误)Object obj2 =42;System.out.print("场景3结果:");switch(obj2){caseInteger i when i <18->System.out.println("未成年,年龄:"+ i);caseInteger i when i >=18&& i <=60->System.out.println("成年,年龄:"+ i);caseInteger i ->System.out.println("老年,年龄:"+ i);caseString s when s.length()>5->System.out.println("字符串长度大于5:"+ s);default->System.out.println("未知数据");}// 场景4:结合密封类(穷尽性检查,编译器自动校验)Shape shape =newCircle(5.0);double area =switch(shape){caseCircle c ->Math.PI* c.radius()* c.radius();caseRectangle r -> r.width()* r.height();caseTriangle t ->(t.base()* t.height())/2.0;// 无需default,编译器会检查密封类的所有子类是否都被覆盖};System.out.println("场景4结果:图形面积:"+String.format("%.2f", area));// 场景5:记录模式匹配(直接解构记录属性)Person person =newPerson("Alice",30);String personInfo =switch(person){casePerson(String name,int age) when age <18->"未成年人:"+ name;casePerson(String name,int age)->"成年人:"+ name +"("+ age +"岁)";};System.out.println("场景5结果:"+ personInfo);}// 密封类:限制子类只能是指定的几个类(配合switch实现穷尽性检查)publicsealedinterfaceShapepermitsCircle,Rectangle,Triangle{}// 密封类的子类,必须使用final、sealed或non-sealed修饰(record默认final,符合要求)publicrecordCircle(double radius)implementsShape{}publicrecordRectangle(double width,double height)implementsShape{}publicrecordTriangle(double base,double height)implementsShape{}// 记录类:用于演示记录模式匹配publicrecordPerson(String name,int age){}}
2.3 运行结果
场景1结果:字符串类型,长度:5 场景2结果:字符串为null 场景3结果:成年,年龄:42 场景4结果:图形面积:78.54 场景5结果:成年人:Alice(30岁) 
2.4 注意事项
  • case 标签的顺序会影响匹配结果,更具体的模式(如带条件守卫的 case)应放在前面,避免被更宽泛的模式覆盖。
  • 当 switch 的表达式为 null 时,会优先匹配 case null,无需在 switch 前添加 Objects.nonNull() 判断。
  • 结合密封类使用时,若遗漏密封类的某个子类,编译器会直接报错(穷尽性检查),避免运行时异常。
  • 模式变量(如 case String s 中的 s)仅在当前 case 分支中有效,不可跨分支使用。
  • 支持两种条件守卫语法:when 子句(更优雅)和 && 逻辑判断(更灵活),可根据场景选择。

3. 记录模式(Record Patterns,JEP 440)—— 简化数据解构

3.1 特性说明

记录模式是 JDK21 正式特性,它扩展了模式匹配的能力,允许直接解构记录(Record)的组件,无需手动调用 record 的 getter 方法,同时支持嵌套解构、部分解构,可轻松提取复杂对象的深层属性,让代码更简洁、更具可读性。该特性从 JDK19 开始预览,经过两轮优化后正式定稿,常与 switch 模式匹配配合使用,是处理不可变数据的最佳实践。

核心价值:

  • 简化记录解构:直接提取 record 的组件,无需调用 xxx() 方法。
  • 支持嵌套解构:可多层嵌套,提取复杂对象的深层属性。
  • 支持部分解构:无需关注所有组件,可忽略不需要的属性。
  • 与 switch/instanceof 无缝集成:实现复杂数据的快速判断和解构。
  • 编译时安全:通过编译时检查确保属性访问的正确性,避免类型错误。
3.2 代码样例(基础解构 + 嵌套解构 + 部分解构)
importjava.time.LocalDate;/** * 记录(Record)模式匹配 - 解构演示(修复未命名模式兼容问题) * 要求:Java 21+ */publicclassRecordPatternMatchingDemo{// 基础记录(无嵌套)publicrecordPoint(int x,int y){}// 嵌套记录(包含Point记录)publicrecordColoredPoint(Point point,String color){}// 多层嵌套记录(包含ColoredPoint记录)publicrecordRectangle(ColoredPoint upperLeft,ColoredPoint lowerRight){}// 复杂业务记录(包含嵌套记录和普通类型)publicrecordOrder(String orderId,LocalDate createTime,ColoredPoint deliveryAddress){}publicstaticvoidmain(String[] args){// 场景1:基础记录解构(直接提取Point的x和y)Point point =newPoint(10,20);if(point instanceofPoint(int x,int y)){System.out.println("场景1:基础解构 - x="+ x +", y="+ y);}// 场景2:嵌套记录解构(提取ColoredPoint中的Point组件)ColoredPoint coloredPoint =newColoredPoint(newPoint(5,8),"red");if(coloredPoint instanceofColoredPoint(Point(int x,int y),String color)){System.out.println("场景2:嵌套解构 - x="+ x +", y="+ y +", color="+ color);}// 场景3:多层嵌套解构(提取Rectangle中的深层Point组件)Rectangle rectangle =newRectangle(newColoredPoint(newPoint(1,2),"blue"),newColoredPoint(newPoint(3,4),"blue"));if(rectangle instanceofRectangle(ColoredPoint(Point(int x1,int y1),String color1),ColoredPoint(Point(int x2,int y2),String color2))){System.out.println("场景3:多层嵌套解构 - "+"左上角("+ x1 +","+ y1 +","+ color1 +"), "+"右下角("+ x2 +","+ y2 +","+ color2 +")");}// 场景4:部分解构(替换_为显式命名的忽略变量,解决兼容问题)Order order =newOrder("ORD-2024-001",LocalDate.now(),newColoredPoint(newPoint(100,200),"green"));if(order instanceofOrder(String orderId,LocalDate ignoreCreateTime,ColoredPoint(Point(int x,int y),String ignoreColor))){// 忽略ignoreCreateTime和ignoreColor,仅使用需要的变量System.out.println("场景4:部分解构 - 订单ID:"+ orderId +", 配送坐标:("+ x +","+ y +")");}// 场景5:结合switch模式匹配(同样替换_为ignore变量)Object obj =newColoredPoint(newPoint(3,5),"green");String deconstructionResult =switch(obj){casePoint(int x,int y)->"Point: ("+ x +","+ y +")";caseColoredPoint(Point(int x,int y),String color)->"ColoredPoint: ("+ x +","+ y +"), "+ color;caseRectangle(ColoredPoint p1,ColoredPoint p2)->"Rectangle: "+ p1 +", "+ p2;caseOrder(String orderId,LocalDate ignoreTime,ColoredPoint ignoreAddr)->"Order: "+ orderId;default->"未知记录类型";};System.out.println("场景5:switch结合记录模式 - "+ deconstructionResult);}}
3.3 运行结果
场景1:基础解构 - x=10, y=20 场景2:嵌套解构 - x=5, y=8, color=red 场景3:多层嵌套解构 - 左上角(1,2,blue), 右下角(3,4,blue) 场景4:部分解构 - 订单ID:ORD-2024-001, 配送坐标:(100,200) 场景5:switch结合记录模式 - ColoredPoint: (3,5), green 
3.4 注意事项
  • 记录模式仅适用于 record 类,普通类无法使用(record 类是不可变的,组件具有固定的 getter 方法)。
  • 嵌套解构时,需保证每层的模式匹配正确,若某一层不匹配,则整个模式匹配失败。
  • 可使用 var 简化变量声明,例如 Point(var x, var y),编译器会自动推断变量类型。
  • 部分解构时,可使用下划线 _ 表示不需要的组件,提高代码可读性。
  • 记录模式可与 instanceof、switch 配合使用,尤其适合处理 DTO、VO 等不可变数据对象。

4. 密封类(Sealed Classes,JEP 409)—— 控制类继承

4.1 特性说明

密封类在 JDK21 中正式定型,它允许开发者限制类或接口的继承/实现范围,明确指定哪些类可以继承它,避免不必要的继承和扩展,提升代码的可维护性和安全性。密封类从 JDK15 开始预览,经过多轮优化后正式成为标准特性,常与 switch 模式匹配配合使用,实现穷尽性检查,确保分支逻辑覆盖所有可能的子类。

核心规则:

  • 密封类使用 sealed 修饰,通过 permits 关键字指定允许继承的子类。
  • 密封类的子类必须使用以下三种修饰符之一:
    • final:不可再被继承。
    • sealed:可继续限制子类(需再次使用 permits 指定)。
    • non-sealed:开放继承,允许任意类继承。
  • 密封接口的实现类,也需遵循上述修饰符规则。
  • 密封类及其子类必须在同一个包下(若使用模块,需在同一个模块下)。
4.2 代码样例(密封类 + 密封接口 + 多层密封)
packageorg.example.model;/** * 密封类 + 密封接口 + 多层密封 演示 * 要求:Java 21+ */publicclassSealedClassDemo{publicstaticvoidmain(String[] args){// 测试密封类(静态内部类可直接实例化)Animal cat =newCat();Animal dog =newDog();Animal puppy =newPuppy();Animal bird =newBird();System.out.println("猫的叫声:"+ cat.makeSound());System.out.println("狗的叫声:"+ dog.makeSound());System.out.println("小狗的叫声:"+ puppy.makeSound());System.out.println("鸟的叫声:"+ bird.makeSound());// 测试密封接口Fruit apple =newApple();Fruit banana =newBanana();Fruit orange =newOrange();System.out.println("苹果的颜色:"+ apple.getColor());System.out.println("香蕉的颜色:"+ banana.getColor());System.out.println("橙子的颜色:"+ orange.getColor());// 测试穷尽性检查(switch + 密封类)printAnimalType(cat);printAnimalType(dog);printAnimalType(puppy);printAnimalType(bird);}// 给sealed类添加abstract修饰符(包含抽象方法必须声明为抽象类)// 密封+抽象类:仅允许 Cat、Dog、Bird 继承publicstaticabstractsealedclassAnimalpermitsCat,Dog,Bird{publicabstractStringmakeSound();}// final 子类:不可再继承publicstaticfinalclassCatextendsAnimal{@OverridepublicStringmakeSound(){return"喵喵喵";}}// sealed 子类:可继续限制子类(仅允许 Puppy 继承)publicstaticsealedclassDogextendsAnimalpermitsPuppy{@OverridepublicStringmakeSound(){return"汪汪汪";}}// Dog 的子类,final 修饰publicstaticfinalclassPuppyextendsDog{@OverridepublicStringmakeSound(){return"呜呜呜(小狗叫)";}}// non-sealed 子类:开放继承,允许任意类继承publicstaticnon-sealedclassBirdextendsAnimal{@OverridepublicStringmakeSound(){return"叽叽喳喳";}}// 密封接口:仅允许 Apple、Banana、Orange 实现publicstaticsealedinterfaceFruitpermitsApple,Banana,Orange{StringgetColor();}publicstaticfinalclassAppleimplementsFruit{@OverridepublicStringgetColor(){return"红色";}}publicstaticfinalclassBananaimplementsFruit{@OverridepublicStringgetColor(){return"黄色";}}publicstaticnon-sealedclassOrangeimplementsFruit{@OverridepublicStringgetColor(){return"橙色";}}// 结合switch实现穷尽性检查// 调整匹配顺序(先匹配Puppy,再匹配Dog),让编译器识别穷尽性privatestaticvoidprintAnimalType(Animal animal){String type =switch(animal){caseCat ignore ->"猫科动物";casePuppy ignore ->"犬科动物(小狗)";// 先匹配Puppy(Dog的子类)caseDog ignore ->"犬科动物(成年犬)";// 再匹配普通DogcaseBird ignore ->"鸟类动物";};System.out.println("动物类型:"+ type);}}
4.3 运行结果
猫的叫声:喵喵喵 狗的叫声:汪汪汪 小狗的叫声:呜呜呜(小狗叫) 鸟的叫声:叽叽喳喳 苹果的颜色:红色 香蕉的颜色:黄色 橙子的颜色:橙色 动物类型:猫科动物 动物类型:犬科动物(成年犬) 动物类型:犬科动物(小狗) 动物类型:鸟类动物 
4.4 注意事项
  • 密封类的 permits 关键字后,必须列出所有允许继承的子类,不能遗漏。
  • 密封类不能是 final 的(final 类不可继承,与密封类的设计初衷冲突)。
  • 非密封子类(non-sealed)可以被任意类继承,打破密封类的限制,适合需要灵活扩展的场景。
  • 密封类与 switch 模式匹配配合时,编译器会自动检查是否覆盖所有子类,避免遗漏分支。
  • 密封接口的实现类同样需要遵循修饰符规则,确保接口的实现范围可控。

5. 分代 ZGC(Generational ZGC,JEP 439)—— 提升 GC 性能

5.1 特性说明

ZGC 是 Java 推出的低延迟垃圾收集器,JDK21 正式将分代 ZGC 从实验特性转为正式特性(JEP 439 Final 状态),其核心是将堆内存分为年轻代和老年代,针对不同代的对象采用差异化收集策略:

  • 年轻代对象生命周期短,采用频繁、快速的收集策略;
  • 老年代对象生命周期长,采用低频率、高效率的收集策略。

这一优化极大提升了垃圾收集的吞吐量和响应速度,尤其适合大堆内存场景(如 10GB 以上堆内存)。

核心优势:

  • 低延迟:GC 停顿时间控制在毫秒级甚至亚毫秒级,几乎不影响程序正常运行;
  • 高吞吐量:分代收集减少了不必要的垃圾扫描范围,提升垃圾收集效率;
  • 大堆支持:64 位系统下最大支持 16TB 堆内存(设计上限),可轻松应对大内存应用场景;
  • 并发处理:年轻代和老年代的核心垃圾收集流程均为并发执行,应用线程无需长时间阻塞;
  • 动态内存布局:可根据应用实际内存需求自动调整年轻代 / 老年代的大小;
  • 无缝迁移:无需修改业务代码,仅通过 JVM 参数即可启用,无侵入性。

官方规划:OpenJDK 计划在 JDK25 及后续版本中将分代 ZGC 设为默认垃圾收集器,后续会逐步移除非分代 ZGC 实现,ZGenerational 选项也将不再需要手动配置。

5.2 启用方式(JVM 参数)
# 启用分代 ZGC(JDK21 正式特性,无需解锁实验性选项) -XX:+UseZGC -XX:+ZGenerational # 可选参数(优化大堆场景) -XX:ZHeapSize=10G # 设置堆内存总大小为10G -XX:MaxGCPauseMillis=10 # 建议最大GC停顿时间(ZGC 会尽力满足,非强制) -XX:ZYoungGenSize=2G # 设置年轻代初始大小为2G(默认自动动态调整,无需手动配置) 
5.3 实际应用示例
# 运行Spring Boot应用并启用分代ZGC(JDK21+) java -XX:+UseZGC -XX:+ZGenerational -XX:ZHeapSize=20G -jar your-app.jar # 进阶配置(指定年轻代大小+停顿时间目标) java -XX:+UseZGC -XX:+ZGenerational -XX:ZHeapSize=32G -XX:ZYoungGenSize=4G -XX:MaxGCPauseMillis=5 -jar your-app.jar 
5.4 注意事项
  • 版本要求:分代 ZGC 仅在 JDK21 及以上版本支持,JDK21 之前的 ZGC 无分代收集能力;
  • 排他性:启用分代 ZGC 后,不可同时启用 G1、Shenandoah 等其他垃圾收集器,避免 JVM 参数冲突;
  • 场景适配:适合大堆内存场景(堆内存 ≥ 4GB),小堆内存场景(<4GB)使用 G1 收集器性价比更高;
  • 参数原则:分代 ZGC 会自动动态调整年轻代 / 老年代大小,除非有明确的性能调优需求,否则无需手动配置 ZYoungGenSize
  • 技术依赖:依赖染色指针(Colored Pointers)和读屏障(Load Barriers)技术,64 位主流操作系统 / 虚拟机环境默认已启用,无需额外配置;
  • 堆上限:ZGC 设计最大堆大小为 16TB(JDK21),实际可用上限受操作系统、硬件物理内存限制,无法突破 16TB。

总结

  1. 核心参数修正:
    • 移除多余的 -XX:+UnlockExperimentalVMOptions(JDK21 分代 ZGC 是正式特性,无需解锁);
    • 错误参数 ZYoungGenerationSize → 正确参数 ZYoungGenSizeGenGeneration 的官方缩写,JVM 仅识别缩写形式);
    • ZGC 最大堆上限为 16TB(非 “16TB 以上”)。
  2. 启用原则:优先使用默认参数,仅在大堆场景(≥4GB)手动调整堆总大小(ZHeapSize),年轻代大小(ZYoungGenSize)无需主动配置。
  3. 关键验证:若在 JVM 启动时传入 ZYoungGenerationSize,会直接提示 Unrecognized VM option 'ZYoungGenerationSize',而 ZYoungGenSize 是 JDK21 分代 ZGC 的官方标准参数。

6. 序列化集合(Sequenced Collections,JEP 431)—— 有序集合标准化

6.1 特性说明
  • JDK21 正式引入 Sequenced Collections(序列化集合,又称有序集合),这是一种具有确定出现顺序(encounter order)的集合,无论遍历多少次,元素的出现顺序始终固定。它提供了统一的接口和方法来处理集合的首尾元素、反向视图等,解决了以往不同集合(List、Set、Map)处理顺序操作的 API 不一致问题。核心优势:核心接口关系:
    • 统一接口:提供 SequencedCollectionSequencedSetSequencedMap 三大核心接口,标准化顺序操作。
    • 有序性保证:元素插入顺序与遍历顺序一致,支持首尾元素快速访问。
    • 线程安全:部分实现(如 ConcurrentLinkedDeque)通过 Sequenced Locks 机制实现线程安全,支持多线程并发读写。
    • 可扩展性:设计考虑了大量数据的并发访问,适合高并发场景。
    • 反向视图:通过 reversed() 方法快速获取反向顺序的集合视图,无需手动反转。
    • SequencedCollection:继承 Collection,基础有序集合接口。
    • SequencedSet:继承 SequencedCollectionSet,有序不重复集合接口。
    • SequencedMap:继承 Map,有序键值对映射接口。
6.2 代码样例(三大接口完整演示)
importjava.util.LinkedHashMap;importjava.util.LinkedHashSet;importjava.util.LinkedList;importjava.util.SequencedCollection;importjava.util.SequencedMap;importjava.util.SequencedSet;/** * 有序集合(Sequenced Collections)三大接口演示 * 注意:使用LinkedList(而非ArrayList),因为其支持addFirst()/addLast() */publicclassSequencedCollectionsDemo{publicstaticvoidmain(String[] args){// 场景1:SequencedCollection 演示(以 LinkedList 为例,支持addFirst/addLast)System.out.println("=== 场景1:SequencedCollection 演示 ===");SequencedCollection<Integer> collection =newLinkedList<>(); collection.add(1); collection.addFirst(0);// 添加到头部(LinkedList支持,ArrayList不支持) collection.addLast(2);// 添加到尾部System.out.println("集合元素:"+ collection);System.out.println("第一个元素:"+ collection.getFirst());System.out.println("最后一个元素:"+ collection.getLast());SequencedCollection<Integer> reversedCollection = collection.reversed();System.out.println("反向集合:"+ reversedCollection); collection.removeFirst();// 删除头部元素 collection.removeLast();// 删除尾部元素System.out.println("删除首尾后:"+ collection);// 场景2:SequencedSet 演示(以 LinkedHashSet 为例)System.out.println("\n=== 场景2:SequencedSet 演示 ===");SequencedSet<Integer> set =newLinkedHashSet<>(); set.add(3); set.add(1); set.add(2); set.addFirst(0);// 添加到头部 set.addLast(4);// 添加到尾部System.out.println("Set元素(有序不重复):"+ set);System.out.println("第一个元素:"+ set.getFirst());System.out.println("最后一个元素:"+ set.getLast());SequencedSet<Integer> reversedSet = set.reversed();System.out.println("反向Set:"+ reversedSet);// 场景3:SequencedMap 演示(以 LinkedHashMap 为例)System.out.println("\n=== 场景3:SequencedMap 演示 ===");SequencedMap<Integer,String> map =newLinkedHashMap<>(); map.put(1,"Apple"); map.put(2,"Banana"); map.put(3,"Orange"); map.putFirst(0,"Grape");// 添加到头部 map.putLast(4,"Mango");// 添加到尾部System.out.println("Map元素:"+ map);System.out.println("第一个键值对:"+ map.firstEntry());System.out.println("最后一个键值对:"+ map.lastEntry());// 获取有序的key、value、entry集合System.out.println("有序key集合:"+ map.sequencedKeySet());System.out.println("有序value集合:"+ map.sequencedValues());System.out.println("有序entry集合:"+ map.sequencedEntrySet());// 移除首尾键值对 map.pollFirstEntry();// 移除并返回第一个键值对 map.pollLastEntry();// 移除并返回最后一个键值对System.out.println("移除首尾后:"+ map);// 反向MapSequencedMap<Integer,String> reversedMap = map.reversed();System.out.println("反向Map:"+ reversedMap);}}
6.3 运行结果
=== 场景1:SequencedCollection 演示 === 集合元素:[0, 1, 2] 第一个元素:0 最后一个元素:2 反向集合:[2, 1, 0] 删除首尾后:[1] === 场景2:SequencedSet 演示 === Set元素(有序不重复):[0, 3, 1, 2, 4] 第一个元素:0 最后一个元素:4 反向Set:[4, 2, 1, 3, 0] === 场景3:SequencedMap 演示 === Map元素:{0=Grape, 1=Apple, 2=Banana, 3=Orange, 4=Mango} 第一个键值对:0=Grape 最后一个键值对:4=Mango 有序key集合:[0, 1, 2, 3, 4] 有序value集合:[Grape, Apple, Banana, Orange, Mango] 有序entry集合:[0=Grape, 1=Apple, 2=Banana, 3=Orange, 4=Mango] 移除首尾后:{1=Apple, 2=Banana, 3=Orange} 反向Map:{3=Orange, 2=Banana, 1=Apple} 
6.4 注意事项

实现类说明:

  • List 接口继承 SequencedCollectionLinkedList 重写 addFirst()/addLast() 可正常使用,ArrayList 的同名方法抛 UnsupportedOperationException);
  • Deque 接口继承 SequencedCollection(如 ConcurrentLinkedDequeArrayDeque);
  • LinkedHashSetTreeSet(实现 NavigableSet)实现 SequencedSet
  • LinkedHashMapTreeMap(实现 NavigableMap)实现 SequencedMap

线程安全:SequencedCollections 接口本身不保证线程安全,需使用线程安全的实现类(如 ConcurrentLinkedDeque)或手动加锁;ConcurrentLinkedDeque 的线程安全基于 CAS 机制实现,无 “Sequenced Locks” 概念。

反向视图:reversed() 方法返回的是原集合的反向视图(而非新集合),修改原集合会同步影响反向视图,反之亦然。

空集合处理:调用空集合的 getFirst()getLast() 方法会抛出 NoSuchElementException,需提前通过 isEmpty() 判断集合是否为空。

与传统方法对比:

  • addFirst()/addLast() 是对 Deque 同名方法的标准化扩展(覆盖到所有 List),而非 “替代”;
  • sequencedKeySet()Map.keySet()有序增强版(返回 SequencedSet),二者共存,并非 “替代”。

二、预览特性(需启用 --enable-preview 参数)

预览特性是 JDK 中已实现但尚未正式定型的特性,可能在未来版本中优化或调整,使用时需在编译和运行时添加 --enable-preview 参数(具体用法见每个特性的说明)。JDK21 中有多个实用预览特性,以下重点讲解最常用的 5 个。

1. 字符串模板(String Templates,JEP 430)—— 简化字符串拼接

    • 简洁高效:直接嵌入表达式,无需拼接,代码更简洁。
    • 类型安全:表达式类型自动适配,无需手动转换。
    • 支持格式化:通过 FMT 处理器支持格式化说明符(如 %.2f%-10s)。
    • 支持自定义:可实现 StringTemplate.Processor 接口创建自定义处理器。
    • 多行支持:支持多行字符串模板,保留换行和缩进格式。
    • 表达式灵活:支持变量、方法调用、复杂计算、注释等多种表达式形式。
    • STR:基础处理器,自动执行字符串插值,将表达式替换为字符串值。
    • FMT:格式化处理器,支持格式化说明符(如 %.2f%-10s)。
    • RAW:原始处理器,返回 StringTemplate 对象,不自动处理插值,用于自定义处理。
1.1 特性说明

字符串模板是 JDK21 的预览特性,类似 Python 的 f-string、JavaScript 的模板字符串,允许在字符串中直接嵌入表达式,无需使用 + 拼接或 String.format(),支持三种内置模板处理器,同时支持自定义处理器,解决了传统字符串拼接繁琐、易出错、可读性差的问题。核心优势:内置模板处理器:

1.2 代码样例(完整用法演示)
importjava.time.LocalTime;importjava.time.format.DateTimeFormatter;/** * 字符串模板(预览特性)完整用法演示 * 编译命令:javac --enable-preview --release 21 StringTemplateDemo.java * 运行命令:java --enable-preview StringTemplateDemo */publicclassStringTemplateDemo{// 测试方法:用于表达式调用privatestaticStringgetName(){return"Java开发者";}// 自定义模板处理器:转换为大写格式(兼容null参数)privatestaticclassUpperCaseProcessorimplementsStringTemplate.Processor<String,RuntimeException>{@OverridepublicStringprocess(StringTemplate st){// 拼接模板文本和表达式值,转换为大写StringBuilder sb =newStringBuilder();for(int i =0; i < st.fragments().size(); i++){ sb.append(st.fragments().get(i));if(i < st.arguments().size()){Object arg = st.arguments().get(i);// 兼容null参数,避免空指针 sb.append(arg ==null?"null": arg.toString().toUpperCase());}}return sb.toString();}}publicstaticvoidmain(String[] args){// 基础信息String name ="Alice";int age =30;double salary =15000.50;String userInput ="<script>alert('攻击')</script>";// 修正:设置有注入风险的输入// 场景1:STR处理器(基础插值)String info1 =STR."姓名:\{name},年龄:\{age},薪资:\{salary}";System.out.println("场景1:STR基础插值 - "+ info1);// 场景2:STR处理器(表达式调用:变量、方法、计算)String info2 =STR."静态变量:\{name},方法调用:\{getName()},计算结果:\{10 + 20 == 30 ? "正确" : "错误"}";System.out.println("场景2:STR表达式调用 - "+ info2);// 场景3:FMT处理器(格式化)String info3 =FMT."姓名:%-10s\{name},年龄:%03d\{age},薪资:%.2f\{salary}";System.out.println("场景3:FMT格式化 - "+ info3);// 场景4:RAW处理器(原始模板,自定义处理)StringTemplate rawTemplate =RAW."姓名:\{name},年龄:\{age}";System.out.println("场景4:RAW原始文本 - "+ rawTemplate.text());System.out.println("场景4:RAW插值后(STR处理) - "+STR.process(rawTemplate));// 场景5:多行字符串模板String info4 =STR.""" 个人信息: 姓名:\{name} 年龄:\{age}岁 薪资:\{salary}元/月 当前时间:\{ // 多行表达式支持注释 DateTimeFormatter.ofPattern("HH:mm:ss") .format(LocalTime.now()) } """;System.out.println("场景5:多行模板 - "+ info4);// 场景6:自定义模板处理器(修正调用语法)UpperCaseProcessor upperProcessor =newUpperCaseProcessor();StringTemplate upperTemplate =STR."姓名:\{name},年龄:\{age}";String info5 = upperProcessor.process(upperTemplate);System.out.println("场景6:自定义处理器(大写) - "+ info5);// 场景7:安全说明:STR/FMT不自动转义,防注入需自定义处理器String unsafeInfo =STR."用户输入:\{userInput}";System.out.println("场景7:未转义输入 - "+ unsafeInfo);}}
1.3 运行结果
场景1:STR基础插值 - 姓名:Alice,年龄:30,薪资:15000.5 场景2:STR表达式调用 - 静态变量:Alice,方法调用:Java开发者,计算结果:正确 场景3:FMT格式化 - 姓名:Alice ,年龄:030,薪资:15000.50 场景4:RAW原始文本 - 姓名:\{name},年龄:\{age} 场景4:RAW插值后(STR处理) - 姓名:Alice,年龄:30 场景5:多行模板 - 个人信息: 姓名:Alice 年龄:30岁 薪资:15000.5元/月 当前时间:14:35:22 场景6:自定义处理器(大写) - 姓名:ALICE,年龄:30 场景7:未转义输入 - 用户输入:<script>alert('攻击')</script> 
1.4 注意事项
  • 启用方式:必须在编译和运行时添加 --enable-preview --release 21 参数(JDK22/23 同理),否则会提示 “字符串模板是预览特性,需启用预览模式”;
  • 安全说明:STR 和 FMT 处理器不会自动转义特殊字符(如 <>/'/"),处理用户输入时需自定义处理器实现 XSS / 注入防护;
  • 多行模板:使用 “”" 包裹,保留换行和缩进格式,表达式嵌入方式与单行一致,多行表达式内可写注释;
  • 自定义处理器:
    • 实现 StringTemplate.Processor 接口的 process 方法,入参为 StringTemplate(包含模板文本片段 fragments() 和表达式参数 arguments());
    • 调用时需先通过 STR/RAW 创建 StringTemplate 对象,再传入处理器的 process() 方法;
    • 需处理参数为 null 的情况,避免空指针异常;
  • 表达式限制:模板中的表达式不能包含未声明的变量,表达式执行抛出的异常会直接传播,需手动捕获处理;
  • 版本说明:JDK21/22/23 中 String Templates 均为预览特性,正式版尚未发布,生产环境慎用。

2. 结构化并发(Structured Concurrency,JEP 453)—— 简化并发任务管理

2.1 特性说明

结构化并发是 JDK21 的预览特性(JDK23 正式转正),旨在简化多线程并发任务的管理,解决传统并发编程中线程泄漏、任务间依赖混乱、异常处理繁琐等问题。它将一组相关的并发任务视为一个整体,统一管理生命周期(启动、执行、取消、终止),确保所有子任务完成后主线程再继续执行,同时支持异常统一捕获和任务取消扩散,让并发代码更具可读性、可维护性和安全性。
核心优势:

  • 生命周期统一:子任务与父任务绑定,父任务取消 / 终止时,所有子任务自动取消,避免线程泄漏;
  • 异常集中处理:子任务抛出的异常会被收集到 scope 中,可统一获取和处理,无需单独捕获;
  • 任务依赖清晰:通过结构化层级明确任务间的父子关系,避免并发任务逻辑混乱;
  • 与虚拟线程兼容:可灵活搭配虚拟线程使用,轻松管理大量并发子任务,提升高并发场景性能;
  • 取消扩散:父任务取消时,会自动中断所有未完成的子任务,避免资源浪费;
  • 自动资源管理:结合 try-with-resources 语法,确保 scope 自动关闭,杜绝线程泄漏。
2.2 代码样例(基础使用 + 异常处理 + 取消任务)
importjava.util.concurrent.ExecutionException;importjava.util.concurrent.StructuredTaskScope;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.TimeoutException;/** * 结构化并发(预览特性)演示 * 编译命令:javac --enable-preview --release 21 StructuredConcurrencyDemo.java * 运行命令:java --enable-preview StructuredConcurrencyDemo */publicclassStructuredConcurrencyDemo{publicstaticvoidmain(String[] args)throwsInterruptedException{// 场景1:基础结构化并发(父子任务绑定)System.out.println("=== 场景1:基础结构化并发 ===");try(var scope =newStructuredTaskScope.ShutdownOnSuccess<String>()){// 提交子任务1(虚拟线程需显式指定线程工厂,默认平台线程) scope.fork(()->{TimeUnit.MILLISECONDS.sleep(100);return"子任务1执行完成";});// 提交子任务2 scope.fork(()->{TimeUnit.MILLISECONDS.sleep(200);return"子任务2执行完成";});// 等待子任务完成(ShutdownOnSuccess:任一成功则关闭其他任务) scope.join();// 获取成功完成的子任务结果(第一个成功的)System.out.println("成功结果:"+ scope.result());}// try-with-resources 自动关闭scope// 场景2:异常处理(子任务抛出异常,父任务统一捕获)System.out.println("\n=== 场景2:异常处理 ===");try(var scope =newStructuredTaskScope.ShutdownOnFailure()){// 子任务3:抛出异常 scope.fork(()->{TimeUnit.MILLISECONDS.sleep(50);System.out.println("子任务3执行中");thrownewRuntimeException("子任务3执行失败");});// 子任务4:因子任务3失败,会被提前终止,不会执行到打印 scope.fork(()->{TimeUnit.MILLISECONDS.sleep(150);System.out.println("子任务4执行完成");return"子任务4成功";});try{ scope.join();// 等待,ShutdownOnFailure:任一失败则关闭}catch(ExecutionException e){// 捕获封装的异常,实际异常在scope.failures()中System.out.println("捕获到执行异常:"+ e.getMessage());}// 遍历所有子任务的异常(核心:从failures()获取) scope.failures().forEach(ex ->System.out.println("子任务异常:"+ ex.getMessage()));}// 场景3:手动取消任务System.out.println("\n=== 场景3:手动取消任务 ===");try(var scope =newStructuredTaskScope<String>()){// 子任务5:耗时任务var future1 = scope.fork(()->{try{TimeUnit.MILLISECONDS.sleep(1000);return"子任务5执行完成";}catch(InterruptedException e){System.out.println("子任务5被取消");returnnull;}});// 子任务6:快速完成var future2 = scope.fork(()->{TimeUnit.MILLISECONDS.sleep(100);return"子任务6执行完成";});// 等待150ms,或所有子任务完成try{ scope.joinUntil(System.currentTimeMillis()+150);if(future2.isDone()){System.out.println("子任务6结果:"+ future2.resultNow()); scope.shutdown();// 触发子任务中断(子任务5)}}catch(TimeoutException e){System.out.println("等待超时");}}}}
2.3 运行结果
=== 场景1:基础结构化并发 === 成功结果:子任务1执行完成 === 场景2:异常处理 === 子任务3执行中 捕获到执行异常:java.lang.RuntimeException: 子任务3执行失败 子任务异常:子任务3执行失败 === 场景3:手动取消任务 === 子任务6结果:子任务6执行完成 子任务5被取消 
2.4 注意事项
  • 启用方式:需在编译和运行时添加 --enable-preview --release 21 参数(JDK22/23 同理,JDK23 该特性已正式发布,无需预览参数);scope 生命周期:必须使用 try-with-resources 语法,确保自动关闭(触发未完成子任务中断);手动关闭需调用 shutdown(),但仍建议配合 try-with-resources;核心 scope 实现:线程模型:fork() 方法默认使用 ForkJoinPool.commonPool()(平台线程),若要使用虚拟线程,需显式指定线程工厂:scope.fork(() -> {...}, Thread.ofVirtual().factory());异常处理:取消机制:shutdown() 仅标记 scope 为关闭状态,子任务需通过捕获 InterruptedException 响应取消(如场景 3 的子任务 5)。
    • ShutdownOnSuccess:任一子任务成功则立即关闭其他任务,优先返回首个成功结果;
    • ShutdownOnFailure:任一子任务失败则立即关闭其他任务,失败信息存入 failures()
    • StructuredTaskScope:基础实现,需手动控制关闭 / 取消,灵活性最高;
    • join() 可能抛出 InterruptedException(线程中断)、ExecutionException(子任务异常封装);
    • 所有子任务的原始异常需通过 scope.failures() 获取,而非直接捕获 join() 的异常;

3. 向量 API(Vector API,JEP 448)—— 提升数值计算性能

3.1 特性说明

向量 API 是 JDK21 的孵化特性(Incubator)(经 JDK16-JDK20 多轮孵化优化),旨在提供一套高效的数值计算 API,充分利用 CPU 的向量指令(如 AVX2、AVX512、NEON),实现单指令多数据(SIMD)并行计算,大幅提升数组、矩阵等数值操作的性能(尤其适合科学计算、机器学习、信号处理等场景)。它提供类型安全的向量操作,避免手动编写汇编代码,同时支持跨平台,无需关注底层 CPU 架构差异。
核心优势:

  • 高性能:直接映射 CPU 向量指令,SIMD 并行计算,比传统循环快数倍(大规模计算场景);
  • 类型安全:强类型向量接口,编译时检查操作合法性,避免类型错误;
  • 跨平台:自动适配 x86、ARM 等不同 CPU 架构,无需修改代码;
  • 易用性:API 设计简洁,支持向量加减乘除、逻辑运算、聚合等常用操作;
  • 自适应:支持 SPECIES_PREFERRED 自动适配当前 CPU 最优向量长度。
3.2 代码样例(向量计算 + 性能对比)
importjdk.incubator.vector.FloatVector;importjdk.incubator.vector.VectorSpecies;importjava.util.Arrays;/** * 向量API(孵化特性)演示 + 性能对比 * 编译命令:javac --release 21 --add-modules jdk.incubator.vector VectorAPIDemo.java * 运行命令:java --add-modules jdk.incubator.vector VectorAPIDemo */publicclassVectorAPIDemo{// 定义向量物种:自动适配当前CPU最优长度(推荐),float类型privatestaticfinalVectorSpecies<Float>SPECIES=FloatVector.SPECIES_PREFERRED;// 也可手动指定:FloatVector.SPECIES_128(128位,4个float)、FloatVector.SPECIES_256(256位,8个float)// 传统循环计算:两个float数组相加publicstaticvoidarrayAddTraditional(float[] a,float[] b,float[] result){for(int i =0; i < a.length; i++){ result[i]= a[i]+ b[i];}}// 向量API计算:两个float数组相加(修正length()为length)publicstaticvoidarrayAddVector(float[] a,float[] b,float[] result){int i =0;// 向量批量处理(每次处理SPECIES.length个元素)int upperBound =SPECIES.loopBound(a.length);for(; i < upperBound; i +=SPECIES.length){// 修正:length(属性)而非length()// 从数组中加载向量FloatVector va =FloatVector.fromArray(SPECIES, a, i);FloatVector vb =FloatVector.fromArray(SPECIES, b, i);// 向量相加,结果存储到数组 va.add(vb).intoArray(result, i);}// 处理剩余不足一个向量长度的元素for(; i < a.length; i++){ result[i]= a[i]+ b[i];}}publicstaticvoidmain(String[] args){// 初始化两个大数组(1000万元素)int size =10_000_000;float[] a =newfloat[size];float[] b =newfloat[size];float[] resultTraditional =newfloat[size];float[] resultVector =newfloat[size];// 填充数据(固定值,避免随机数影响性能)Arrays.fill(a,1.23f);Arrays.fill(b,4.56f);// JIT预热:先运行一次,避免编译开销影响性能测试arrayAddTraditional(a, b, resultTraditional);arrayAddVector(a, b, resultVector);// 测试传统循环性能(多次运行取平均)long start =System.currentTimeMillis();for(int j =0; j <5; j++){arrayAddTraditional(a, b, resultTraditional);}long traditionalTime =(System.currentTimeMillis()- start)/5;System.out.println("传统循环平均耗时:"+ traditionalTime +"ms");// 测试向量API性能(多次运行取平均) start =System.currentTimeMillis();for(int j =0; j <5; j++){arrayAddVector(a, b, resultVector);}long vectorTime =(System.currentTimeMillis()- start)/5;System.out.println("向量API平均耗时:"+ vectorTime +"ms");// 验证结果一致性boolean isEqual =Arrays.equals(resultTraditional, resultVector);System.out.println("结果是否一致:"+ isEqual);// 打印向量物种信息(当前CPU适配的长度)System.out.println("当前CPU最优向量长度(float元素数):"+SPECIES.length);}}
3.3 运行结果
传统循环平均耗时:30ms 向量API平均耗时:7ms 结果是否一致:true 当前CPU最优向量长度(float元素数):8 
3.4 注意事项

启用方式(核心修正):

  • 编译:javac --release 21 --add-modules jdk.incubator.vector VectorAPIDemo.java
  • 运行:java --add-modules jdk.incubator.vector VectorAPIDemo
  • 无需 --enable-preview(孵化特性≠预览特性),需导入 jdk.incubator.vector 包。

向量物种选择:

  • 优先使用 SPECIES_PREFERRED(自动适配当前 CPU 最优向量长度);
  • 手动选择需匹配 CPU 架构(x86 支持 SPECIES_128/256/512,ARM 支持 SPECIES_128/256)。

适用场景:

  • 适合大规模数值计算(数组 / 矩阵运算,元素数 ≥ 10 万);
  • 小规模计算(<1 万元素)因向量初始化开销,性能可能不如传统循环。

数据类型支持:仅支持 floatdoubleintlong 等基本数值类型,不支持引用类型。

性能优化:

  • 数组长度尽量为向量长度的整数倍,减少剩余元素循环;
  • 性能测试需做 JIT 预热(先运行几次),避免编译开销导致数据失真;
  • 确保 CPU 开启向量指令集(如 x86 的 AVX2/AVX512),否则性能无提升。

三、总结

JDK21 作为 LTS 版本,核心正式特性聚焦于提升开发效率、程序性能和代码安全性,虚拟线程、模式匹配 for switch、记录模式、密封类、分代 ZGC、序列化集合六大正式特性,可直接应用于生产环境,尤其适合高并发、大数据、企业级应用场景;五大预览特性则为后续开发提供了更多可能性,可根据项目需求选择性试用(需注意预览特性可能在未来版本中调整)。

本文所有代码均经过 JDK21 环境测试,可直接复制运行,建议开发者结合实际项目场景,合理运用这些新特性,提升开发效率和程序性能。后续可持续关注 JDK 版本更新,及时掌握新特性的优化和迭代。

Read more

医疗AI场景下算法编程的深度解析(2026新生培训讲稿)(八)

医疗AI场景下算法编程的深度解析(2026新生培训讲稿)(八)

第15章 模型融合与集成策略 在机器学习竞赛和实际应用中,模型融合(Model Ensemble)是提升预测性能的利器。通过组合多个不同的基模型,集成策略能够综合各个模型的优势,抵消单个模型的偏差和方差,从而获得比任何单一模型更稳定、更准确的预测结果。在医疗AI领域,模型融合同样具有重要价值——面对复杂多模态的医疗数据,单一模型往往难以全面捕捉所有信息,而融合多个异质模型可以提升诊断的鲁棒性和准确性。本章将从集成学习的基本思想出发,系统介绍常见的模型融合方法,包括投票法、平均法、Stacking、Blending等,并通过实战案例展示如何构建融合模型来提升疾病预测性能。 15.1 集成学习的基本思想 集成学习(Ensemble Learning)的核心思想是“三个臭皮匠,顶个诸葛亮”——通过结合多个学习器来完成学习任务,通常可以获得比单一学习器更优越的泛化性能。根据个体学习器的生成方式,集成学习主要分为两大类: * Bagging:并行训练多个独立的基学习器,然后通过平均或投票进行结合。典型代表是随机森林。Bagging主要降低方差。 * Boosting:串行训练基学习

By Ne0inhk

保姆级教程:Windows本地部署Ollama+OpenClaw,打造你的AI赚钱系统(APP开发/量化/小说/剪辑)

摘要:想用AI搞钱但卡在技术门槛?本文手把手教你用一台Windows电脑,零成本本地部署Ollama大模型+OpenClaw智能中枢,赋予AI开发APP、量化分析、编写小说、剪辑辅助等“赚钱技能”。全程无需编程基础,跟着鼠标点、照着命令敲,即可拥有24小时待命的AI员工。 一、写在前面 很多朋友对AI变现跃跃欲试,却常被这些问题劝退: * 云端部署太贵,API调用怕浪费钱 * 技术文档看不懂,不知道从哪下手 * 数据隐私担忧,不敢把敏感资料上传 其实,你手头那台Windows电脑完全能胜任!本文将带你搭建一套完全本地化、免费、可扩展的AI生产力系统,让AI帮你写代码、分析表格、生成文案、处理视频,真正把AI变成你的“赚钱工具”。 系统架构: * 本地大脑:Ollama + DeepSeek模型,负责理解任务、生成内容 * 智能中枢:OpenClaw(原名OpenClaude),负责调用各类工具(Skill) * 赚钱技能:通过安装Skill包,让AI具备特定领域的实操能力 适用人群:

By Ne0inhk
人工智能:自然语言处理在客户服务领域的应用与实战

人工智能:自然语言处理在客户服务领域的应用与实战

人工智能:自然语言处理在客户服务领域的应用与实战 学习目标 💡 理解自然语言处理(NLP)在客户服务领域的应用场景和重要性 💡 掌握客户服务领域NLP应用的核心技术(如聊天机器人、意图识别、情感分析) 💡 学会使用前沿模型(如BERT、GPT-3)进行客户服务文本分析 💡 理解客户服务领域的特殊挑战(如实时性要求、多语言处理、用户体验) 💡 通过实战项目,开发一个客户服务聊天机器人应用 重点内容 * 客户服务领域NLP应用的主要场景 * 核心技术(聊天机器人、意图识别、情感分析) * 前沿模型(BERT、GPT-3)在客户服务领域的使用 * 客户服务领域的特殊挑战 * 实战项目:客户服务聊天机器人应用开发 一、客户服务领域NLP应用的主要场景 1.1 聊天机器人 1.1.1 聊天机器人的基本概念 聊天机器人是通过自然语言与用户进行交互的程序。在客户服务领域,聊天机器人的主要应用场景包括: * 客户服务:回答客户的问题(如“如何退货”、“商品价格”

By Ne0inhk
AI 时代,鸿蒙 App 还需要传统导航结构吗?

AI 时代,鸿蒙 App 还需要传统导航结构吗?

子玥酱(掘金 / 知乎 / ZEEKLOG / 简书 同名) 大家好,我是子玥酱,一名长期深耕在一线的前端程序媛 👩‍💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。 我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括前端工程化、小程序、React / RN、Flutter、跨端方案, 在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。 技术方向:前端 / 跨端 / 小程序 / 移动端工程化 内容平台:掘金、知乎、ZEEKLOG、简书 创作特点:实战导向、源码拆解、少空谈多落地 文章状态:长期稳定更新,大量原创输出 我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、

By Ne0inhk