面试官问:JDK 17/21核心新特性(虚拟线程/Record/密封类)?一张图+办公流程比喻,彻底拿下这道必考题(附图解+比喻+避坑指南)
预计阅读:14分钟
📌 你是不是也这样:还在用JDK 8,面试官一问“JDK 17和21有哪些新特性”就只知道“好像有个虚拟线程”,具体怎么回事完全讲不清楚?
今天一张图 + 一个办公流程故事 + 深度解析 + 五道追问,彻底拿下这道题。
📝摘要:JDK 17和21是Java最新的两个LTS版本,标志着Java从“企业级稳重”走向“现代化高效”。JDK 17带来Record类(告别POJO样板代码)、密封类(精准控制继承边界)、模式匹配(简化类型判断与解构);JDK 21带来虚拟线程(百万级并发,彻底改变高并发编程范式)、结构化并发(子任务生命周期自动管理)、顺序集合(统一有序集合操作)。一句话:JDK 17让代码更简洁安全,JDK 21让并发更轻量强大。
我是折哥,《Java 85题图解版》系列连载中(已更新41题,建议收藏本系列)。
每周2-3篇,85题通关路线一键追完。
👉点击关注,第一时间收到每篇新题推送。
- 上一篇:面试官问:序列化与反序列化(含框架对比)?
- 下一篇预告:面试官问:Java模块化(Project Jigsaw)与反射限制?(待发布)
- 全部85题:点击查看总目录(关注专栏,追更不迷路)
一句话总结:JDK 17让代码更简洁安全,JDK 21让并发更轻量强大。
Record类:一行代码定义不可变数据载体,自动生成构造器、equals、hashCode、toString → 像用标准化表格替代以前要填几页的复杂表单,一句话搞定所有字段。
密封类(Sealed Classes):通过sealed和permits精确控制哪些类可以继承 → 像公司明确指定只有某些部门可以接触核心文件,其他人碰都不能碰。
虚拟线程(Virtual Threads):JVM管理的轻量级线程,创建成本仅几百字节内存 → 像从“一人一车”升级到“共享单车”——几百万辆车成本极低,随用随走。
结构化并发(Structured Concurrency):子任务与父任务生命周期绑定 → 像团队有了组长统筹,一个子任务失败自动取消所有子任务,不会“各干各的”。
背诵口诀:JDK 17 Record、密封、模式匹配;JDK 21 虚拟线程、结构化并发、顺序集合、作用域值。
核心设计理念:JDK 17让代码更简洁安全,JDK 21让并发更轻量强大。
💬 面试还原
面试官:JDK 17和21分别引入了哪些核心新特性?你实际项目中用过哪些?
这是Java后端面试中区分“技术视野”的核心题,直接进入正题。
🧠 一图看懂:JDK 17/21核心新特性全景
🍵 生活比喻:从“写材料”到“开公司”
JDK 17 = 精简办公流程
- Record类 = 标准化表格:以前填个表格要写一堆字段、构造器、getter(POJO),现在一张标准化表格(Record)一行搞定
- 密封类 = 明确谁有权限:以前谁都能继承你的类(开放继承),现在明确指定只有某些人可以继承(密封类),像公司只允许指定部门接触核心文件
- 模式匹配 = 智能识别:以前判断类型要两步(先instanceof再强转),现在一步到位,像安检系统一眼认出你是谁
JDK 21 = 从“卡车运输”到“共享单车”
- 虚拟线程 = 共享单车:传统线程是卡车——一辆车只能一个人用,成本高、数量少(每个线程1MB内存,最多几千个)。虚拟线程是共享单车——几百万辆共享单车,成本极低(几百字节),随用随走
- 结构化并发 = 团队协作有组长:以前多任务并行像“各干各的”,一个失败其他人还在瞎跑。现在像有组长的团队——组长统筹,一个子任务失败全部取消
🔬 JDK 17三大核心特性深度解析
1. Record类 —— 告别POJO样板代码
Record类是不可变数据载体,用一行代码替代几十行POJO代码。
// ❌ 传统POJO:几十行样板代码publicclassPoint{privatefinalintx;privatefinalinty;publicPoint(intx,inty){this.x=x;this.y=y;}publicintx(){returnx;}publicinty(){returny;}@Overridepublicbooleanequals(Objecto){/* ... */}@OverridepublicinthashCode(){/* ... */}@OverridepublicStringtoString(){/* ... */}}// ✅ Record:一行搞定publicrecordPoint(intx,inty){}// 自动生成:构造器、访问器(x()/y())、equals、hashCode、toStringRecord的进阶用法:
// 紧凑构造函数(参数校验)publicrecordPoint(intx,inty){publicPoint{if(x<0||y<0){thrownewIllegalArgumentException("坐标不能为负数");}}}// 实现接口publicinterfaceShape{doublearea();}publicrecordCircle(doubleradius)implementsShape{@Overridepublicdoublearea(){returnMath.PI*radius*radius;}}使用场景:DTO、VO、值对象、API响应对象等纯数据载体。
2. 密封类 —— 精准控制继承边界
密封类通过sealed关键字和permits子句,明确指定哪些类可以继承它,让类型层次结构封闭可知。
// 定义密封类:只有Circle、Rectangle、Triangle可以继承sealedclassShapepermitsCircle,Rectangle,Triangle{}// 子类必须声明为final、sealed或non-sealedfinalclassCircleextendsShape{}finalclassRectangleextendsShape{}finalclassTriangleextendsShape{}密封类的核心价值:与模式匹配结合,编译器能穷尽检查所有子类型,不再需要default分支。
3. 模式匹配 —— 简化类型判断与解构
instanceof模式匹配:判断类型的同时声明变量,消除强制转型。
// ❌ 传统写法if(objinstanceofString){Strings=(String)obj;// 还要强制转型System.out.println(s.length());}// ✅ 模式匹配if(objinstanceofStrings){// 直接声明变量sSystem.out.println(s.length());}switch模式匹配:直接匹配类型,还能解构Record。
Stringdescribe(Shapeshape){returnswitch(shape){caseCirclec->"圆形,半径"+c.radius();caseRectangler->"矩形,"+r.length()+"x"+r.width();caseTrianglet->"三角形";};// 密封类保证穷尽,无需default}🔬 JDK 21四大核心特性深度解析
1. 虚拟线程 —— 百万级并发革命
虚拟线程是JDK 21最具颠覆性的特性,由Project Loom孵化,目标是用同步代码写出异步的吞吐量。
核心对比:
| 维度 | 平台线程(传统) | 虚拟线程 |
|---|---|---|
| 底层映射 | 1:1映射到OS线程 | M:N映射到少量平台线程 |
| 栈内存 | 默认1MB | 初始几百字节,动态伸缩 |
| 最大并发数 | 几千 | 千万级 |
| 阻塞行为 | 阻塞OS线程 | 只阻塞虚拟线程,载体线程释放 |
| 创建成本 | 约1µs + 1MB内存 | 约1µs + 400字节内存 |
虚拟线程的核心机制:
- 虚拟线程由JVM调度,执行在少量载体线程(Carrier Thread)上
- 当虚拟线程执行阻塞I/O时,JVM自动挂起虚拟线程,释放载体线程给其他虚拟线程使用
- I/O完成后,虚拟线程被重新调度到任意载体线程继续执行
- 本质:虚拟线程 = 用户态线程 + JVM级调度器 + 自动挂载/卸载
基本用法:
// 方式1:直接创建Threadvt=Thread.ofVirtual().start(()->{System.out.println("Hello from virtual thread!");});// 方式2:虚拟线程执行器(推荐)try(varexecutor=Executors.newVirtualThreadPerTaskExecutor()){for(inti=0;i<100_000;i++){executor.submit(()->{Thread.sleep(100);// 阻塞时自动释放载体线程returnnull;});}}// try-with-resources自动等待所有虚拟线程结束压测数据:100万个虚拟线程同时sleep 1秒,仅需约1秒完成,内存峰值<1.5GB。
2. 结构化并发 —— 多任务协作有“组长”
传统并发中,子任务“各自为政”——一个失败其他还在跑,资源浪费。结构化并发让子任务与父任务生命周期绑定:父任务失败,所有子任务自动取消。
// 传统方式:需要手动管理Future和异常ExecutorServicees=Executors.newCachedThreadPool();Future<User>user=es.submit(()->fetchUser());Future<Order>order=es.submit(()->fetchOrder());// user失败,order还在继续执行...// 结构化并发try(varscope=newStructuredTaskScope.ShutdownOnFailure()){Future<User>user=scope.fork(()->fetchUser());Future<Order>order=scope.fork(()->fetchOrder());scope.join();// 等待所有子任务scope.throwIfFailed();// 任一失败抛出异常// 全部成功才继续}// 自动取消所有子任务3. 顺序集合 & 作用域值
顺序集合:统一了List、Deque、SortedSet等有序集合的操作接口,新增reversed()、getFirst()、getLast()等方法。
作用域值:替代ThreadLocal,与虚拟线程更兼容,按调用栈传递数据。
🔍 高频面试追问(5道大厂真题)
追问1:虚拟线程和平台线程的本质区别是什么?
回答要点:虚拟线程是JVM调度的用户态线程,不绑定OS线程;平台线程1:1映射到OS线程。
详细回答:
平台线程是重量级的——每个线程默认1MB栈内存,创建数量受OS限制(通常几千个)。虚拟线程是轻量级的——由JVM调度,初始栈仅几百字节,单机可支撑百万级并发。虚拟线程阻塞I/O时会自动释放载体线程,不浪费OS资源。
追问2:虚拟线程适合什么场景?不适合什么场景?
回答要点:适合IO密集型,不适合CPU密集型。
详细回答:
✅ 适合:高并发IO密集型任务(Web服务、RPC调用、数据库查询、微服务网关)——阻塞等待时自动释放载体线程。
❌ 不适合:CPU密集型任务——虚拟线程无法加速计算,大量上下文切换反而降低性能。也不适合
synchronized深度嵌套的遗留代码,可能阻塞载体线程。
追问3:Record类和普通类有什么区别?
回答要点:Record是透明不可变的数据载体,自动生成所有标准方法。
详细回答:
Record类自动生成构造器、访问器、
equals、hashCode和toString。所有字段默认private final,强制不可变。Record隐式继承java.lang.Record,不能继承其他类。适合DTO/VO/值对象等纯数据载体。
追问4:密封类和模式匹配如何配合使用?
回答要点:密封类让编译器知道所有子类型,模式匹配做穷尽检查,无需default。
详细回答:
密封类通过
permits明确指定所有可能的子类。配合模式匹配的switch,编译器能穷尽检查所有子类型——如果漏掉某个子类的case,编译报错。这大幅提升了类型安全性。
追问5:为什么说虚拟线程是“用同步代码写出异步吞吐量”?
回答要点:虚拟线程让阻塞I/O不再阻塞平台线程,代码保持同步风格。
详细回答:
传统异步编程(Netty、CompletableFuture)虽吞吐量高,但代码是“回调地狱”或“链式调用”,调试困难。虚拟线程让每个请求占用一个虚拟线程,执行阻塞I/O时JVM自动挂起并释放载体线程。开发者写的是简单的同步代码,底层自动实现了异步的高吞吐。
💣 避坑指南
| 序号 | 错误做法 | 正确做法 | 后果 |
|---|---|---|---|
| 1 | 在CPU密集型任务中大量使用虚拟线程 | CPU密集型用平台线程或保持线程数≈CPU核心数 | 上下文切换开销大,性能下降 |
| 2 | 在synchronized深度嵌套中使用虚拟线程 | 用ReentrantLock或减少锁嵌套深度 | 可能阻塞载体线程 |
| 3 | 用Record类做可变业务实体 | Record用于不可变数据载体 | 违背设计意图,代码混乱 |
| 4 | 升级到JDK 17/21时不做兼容测试 | 逐步升级,测试遗留框架兼容性 | 反射、字节码库可能不兼容 |
💻 可运行验证代码
// ===== Record类 =====publicrecordUser(Longid,Stringname,Stringemail){}// ===== 密封类 + 模式匹配 =====sealedinterfaceShapepermitsCircle,Rectangle{}publicrecordCircle(doubleradius)implementsShape{}publicrecordRectangle(doublew,doubleh)implementsShape{}Stringdescribe(Shapes){returnswitch(s){caseCirclec->"圆,半径="+c.radius();caseRectangler->"矩形,"+r.w()+"x"+r.h();};}// ===== 虚拟线程 =====try(varexecutor=Executors.newVirtualThreadPerTaskExecutor()){IntStream.range(0,10_000).forEach(i->executor.submit(()->{Thread.sleep(100);System.out.println("Task "+i+" done");returnnull;}));}❓ 评论区挑战
问题:以下关于虚拟线程的说法,哪一个是错误的?
// 创建虚拟线程Threadvt=Thread.ofVirtual().start(()->{Thread.sleep(1000);});A. 虚拟线程由JVM调度,不直接绑定操作系统线程
B. 虚拟线程阻塞I/O时会释放载体线程给其他虚拟线程使用
C. 虚拟线程适合CPU密集型任务,能大幅提升计算性能
D. 一个Java进程可以创建百万级虚拟线程
💬 欢迎在评论区写出你的答案和理由,我会在下一篇文章发布后更新本文,公布答案及错误选项逐项解析。
✅ 答案公布
正确答案:C. 虚拟线程适合CPU密集型任务,能大幅提升计算性能
解析:
- 虚拟线程的优势在IO密集型场景——阻塞时释放载体线程,提高资源利用率
- CPU密集型任务中,虚拟线程无法加速计算,大量上下文切换反而降低性能
- 选项A正确:虚拟线程由JVM调度
- 选项B正确:阻塞I/O时自动释放载体线程
- 选项D正确:虚拟线程可支撑百万级并发
📌 总结
| 版本 | 特性 | 一句话概括 | 核心价值 |
|---|---|---|---|
| JDK 17 | Record类 | 一行代码定义不可变数据载体 | 消除POJO样板代码 |
| JDK 17 | 密封类 | 精准控制继承边界 | 类型层次封闭可知 |
| JDK 17 | 模式匹配 | 简化类型判断与数据解构 | 消除强制转型,提升安全性 |
| JDK 21 | 虚拟线程 | 轻量级用户态线程,百万并发 | 同步代码异步吞吐 |
| JDK 21 | 结构化并发 | 子任务与父任务生命周期绑定 | 自动取消,异常统一处理 |
面试官最看重的三个点:
- JDK 17 vs 21的定位差异:JDK 17让代码更简洁安全,JDK 21让并发更轻量强大
- 虚拟线程适用场景:IO密集型✅ / CPU密集型❌——能准确区分
- 现代Java特性组合:Record+密封类+模式匹配可构建类型安全的领域模型
📚 系列导航
- 上一篇:面试官问:序列化与反序列化(含框架对比)?
- 下一篇预告:面试官问:Java模块化(Project Jigsaw)与反射限制?(待发布)
- 全部85题目录:点击查看(关注专栏,每周2-3篇,一键追更)
📘搭配学习效果更佳
本篇图解帮你快速建立知识画面记忆,如果想深入理解源码实现和实战避坑细节,可以配合姊妹系列《Java 100天进阶之路》对应章节一起学:
从零基础到上岗就业,108篇完整学习地图,每篇标配生活类比 + 可运行代码 + 避坑表 + 面试高频题 + 练习题,不背八股文,真正讲透“为什么”。
👉 《Java 100天进阶之路》完整目录导航
学习建议:图解系列负责“快速建立知识图谱”,进阶系列负责“深入理解原理”,两个系列搭配使用,面试备考效率翻倍。
💬你们公司升级到JDK 17或21了吗?虚拟线程在生产环境用得怎么样?欢迎评论区分享你的实践经验~