面试追问地图

主问题必讲关键点下一层追问
JDK 升级版本收益、依赖兼容、灰度验证反射强封装、Jakarta 迁移
JDK 24/26Stream Gatherers、Class-File API、HTTP/3、G1 同步优化Virtual Thread pinning 修复、Final 字段限制信号
Record数据载体、自动方法、浅不可变与 Lombok、实体类的区别
sealed class封闭类型层次与 enum、普通接口如何选择
模式匹配类型判断、绑定、穷尽检查null 分支、可读性边界
虚拟线程M:N、阻塞卸载、IO 场景pinning、ThreadLocal、连接池
Scoped Value只读上下文、作用域与 ThreadLocal 的生命周期区别
结构化并发作用域绑定、异常短路、父子任务与 CF/ES 对比、与虚拟线程关系
Stream惰性、无副作用、并行池parallelStream 为什么可能变慢
JPMS模块依赖和强封装与 Maven 多模块的区别

现代 Java 特性要回答“是否已正式发布、项目是否实际采用、迁移成本是什么”,不要把预览特性当成稳定能力。


一、版本演进

Java 8、17、21、25 应重点关注什么?

难度 🟡

版本定位面试重点
Java 8长期使用基线Lambda、Stream、Optional、CompletableFuture
Java 17LTSRecord、密封类、模式匹配、强封装
Java 21LTS虚拟线程、Record Pattern、switch 模式匹配
Java 24非 LTSStream Gatherers、Class-File API、Flexible Constructor Bodies 转正、抗量子加密
Java 25LTS模块导入声明、紧凑源文件、Scoped Values、Flexible Constructor Bodies、Compact Object Headers 等 18 个 JEP
Java 26最新非 LTSHTTP/3、Final Mean Final、G1 同步优化、AOT 对象缓存、结构化并发第 6 预览

社招回答应优先说明项目实际版本、升级收益和兼容风险,不需要机械背诵每个 JEP。

从 Java 8 升级到 17/21 要注意什么?

  • JDK 内部 API 强封装,依赖反射访问内部类的库可能失效。
  • javax.*jakarta.* 主要是 Jakarta EE/Spring Boot 3 生态迁移,不是 JDK 自动完成。
  • GC、容器感知和默认参数发生变化。
  • 字节码增强、代理、序列化和监控 Agent 需要验证兼容性。
  • 先升级依赖和构建工具,再升级运行时,并做完整压测和灰度。

JDK 24、26 有什么关键变化?

难度 🟡

JDK 24(2025-03-18)和 JDK 26(2026-03-17)是两个非 LTS 特性版本,但引入了多个对日常开发影响深远的正式特性。JDK 25 是 LTS 版本(2025-09-16),是 JDK 21 之后的下一个长期支持基线。

JDK 24 正式特性(选讲):

JEP特性面试价值
485Stream GatherersStream 终于有了自定义中间操作的官方 API:stream.gather(myGatherer)。之前自定义 Stream 操作只能自己写 Spliterator,现在可以用官方框架组合。★★★★ 面试高频:这是 Stream 自 Java 8 以来最大的扩展,面试官可能会追问 Gatherer 与自定义 Collector 的区别,以及能否替代现有 window/takeWhile 等操作。
484Class-File API — 操作 .class 字节码的官方标准 API,替代 ASM(第三方库)。Spring、Hibernate 等框架的字节码增强可以逐步脱离 ASM 依赖。★★★ 中间件方向:框架开发者必知,业务开发了解即可。
492Flexible Constructor Bodies(转正) — 构造函数中可以在 super() 之前执行逻辑(校验、初始化等),不用再靠静态工厂变通。这是 Java 语言层面少有的构造能力增强。★★★ 面试可能考:与原来 super() 必须在第一行的设计意图对比。
491Synchronize Virtual Threads without Pinning(转正)synchronized 不再导致虚拟线程 pinning,JDK 21 时代最大的虚拟线程坑被填平。★★★★★ 必考:虚拟线程面试的版本口径分水岭。
496/497抗量子加密 — ML-KEM(密钥封装)和 ML-DSA(数字签名)正式纳入 JDK,为后量子时代做准备。★★ 安全方向:知道 JDK 在做此准备即可。

JDK 25 正式特性(选讲,LTS 版本):

JDK 25 是 JDK 21 之后的下一个 LTS,共 18 个 JEP,其中 9 个聚焦性能和运行时。以下是面试价值最高的几个:

JEP特性面试价值
511Module Import Declarations(转正)import module java.base; 一条语句导入整个模块的所有导出包,写 demo 和教学代码方便很多。★★★ 知道这是语法糖,不会替代实际项目中的精确 import。
513Flexible Constructor Bodies(转正) — 与 JDK 24 一致,两个版本确认定型。★★ 参照 JDK 24 条目。
506Scoped Values(转正) — 只读上下文传递(Trace ID、租户),替代 ThreadLocal 用于虚拟线程场景。★★★★ 高频:结合虚拟线程问 ThreadLocal 与 Scoped Value 的区别。
519Compact Object Headers(转正) — 64 位 JVM 对象头从 12-16 字节压缩到约 8-10 字节,降低内存占用。★★★ 性能方向:知道它是通过移除无用对象头位来实现的。
521Generational Shenandoah(转正) — Shenandoah GC 也引入了分代回收(类似 ZGC 的分代演进路线)。★★ GC 方向:JVM 调优面可能会被问到。
512Compact Source Files + Instance Main Methods(转正) — 无 class 包装的脚本式 Java 文件,main 方法不用 public static void 签名,降低入门门槛。★★ 知道即可,面试极少考。
509JFR CPU-Time Profiling(实验性) — JFR 直接采样 CPU 调用栈,定位热点方法不再依赖 async-profiler。★★ 运维方向:线上问题排查能力提升。

JDK 26 正式特性(选讲):

JEP特性面试价值
500Prepare to Make Final Mean Final — 深度反射修改 final 字段会发出警告。这是 Java“完整性优先”原则的关键落地——保护敏感数据不被恶意或意外修改。JDK 26 只发警告,后续版本将完全禁止。★★★★ 必考:Spring/MyBatis 等框架大量用反射设 final 字段,面试官会追问影响范围。
517HTTP/3 for the HTTP Client API(转正)HttpClient 原生支持 HTTP/3(QUIC 协议),微服务间通信延迟更低。★★★ 微服务方向:知道 JDK HTTP Client 补齐了 HTTP/3 支持。
516Ahead-of-Time Object Caching with Any GC(转正) — Project Leyden 成果:预初始化对象缓存,启动时顺序加载,支持 ZGC 等任意 GC。★★★ 性能方向:Java 启动加速进入 Leyden 阶段。
522G1 GC: Improve Throughput by Reducing Synchronization(转正) — 减少应用线程与 GC 线程的同步开销。★★ GC 方向:同等硬件跑更多请求,顺带引出 G1 的 region 和 SATB 原理。
525Structured Concurrency(第六预览) — 结构化并发仍在预览,但已到第 6 版,定型可期。★★★ 并发方向:结合虚拟线程一起问。
530Primitive Types in Patterns, instanceof, and switch(第四预览)switchinstanceof 中直接匹配 intlong 等基本类型,消除装箱拆箱。★★ 语言特性:知道模式匹配正在统一基本类型和引用类型。

二、Record 与不可变数据

Record 是什么?

难度 🟡

Record 用于声明透明的数据载体,编译器自动生成:

  • private final 组件字段。
  • 访问器。
  • 全参数构造器。
  • equalshashCodetoString
public record UserSummary(long id, String name) {}

Record 是浅不可变:字段引用不能更换,但字段指向的可变对象仍可被修改。

Record 适合和不适合什么场景?

适合:

  • DTO、查询结果、事件消息。
  • 值对象和模式匹配。

不适合:

  • 需要无参构造和可变属性的老旧框架。
  • 需要继承具体父类。
  • 内部包含大量可变状态的领域实体。

三、密封类与模式匹配

sealed class 解决什么问题?

密封类显式限制允许继承或实现它的类型:

public sealed interface Result
        permits Success, Failure {}

它适合表达封闭的领域类型集合,让编译器帮助检查分支是否完整。

switch 模式匹配有什么价值?

可以同时完成类型判断、变量绑定和分支处理:

return switch (result) {
    case Success(var data) -> handle(data);
    case Failure(var code, var message) -> fail(code, message);
};

配合 sealed 类型时,编译器可以检查是否穷尽所有合法子类型。

Record Pattern 是什么?

Record Pattern 支持直接解构 Record:

if (obj instanceof Point(int x, int y)) {
    return x + y;
}

它减少手动类型转换和访问器调用,但嵌套模式过深会降低可读性。


四、虚拟线程

虚拟线程是什么?

难度 🔴

虚拟线程是 JVM 调度的轻量级线程,大量虚拟线程复用少量平台线程。

当虚拟线程执行支持的阻塞 IO 时,JVM 可以卸载虚拟线程,让载体线程继续执行其他任务。因此它适合“每请求一线程”的高并发 IO 模型。

虚拟线程适合哪些场景?

适合:

  • HTTP/RPC 请求。
  • 数据库和网络 IO。
  • 大量彼此独立的阻塞任务。

不适合直接提升:

  • CPU 密集计算。
  • 受数据库连接池等外部资源限制的吞吐量。
  • 依赖大量 ThreadLocal 缓存的设计。

虚拟线程是否还需要线程池?

通常不需要为了限制线程数量而池化虚拟线程,可以按任务创建。

真正有限的资源应通过 Semaphore、连接池或限流器限制,例如数据库连接数,而不是用固定大小的虚拟线程池间接限制。

什么是虚拟线程 pinning?

虚拟线程执行某些操作时可能无法从载体线程卸载,导致载体线程被占用。

版本口径要答对:JDK 21 时代最典型的 pinning 场景是在 synchronized 临界区内阻塞;JDK 24 起(JEP 491)synchronized 已不再导致 pinning,剩余场景主要是 native 方法 / 外部函数调用中阻塞。面试说”synchronized 会 pinning”必须带上版本前提,否则暴露知识停在 21。

治理重点:

  • 项目还在 21:缩短临界区、不在锁内做慢 IO,热点锁可换 ReentrantLock
  • 用 JFR 的 jdk.VirtualThreadPinned 事件观察 pinned 情况。
  • 不把平台线程时代的线程池模式原样迁移。

五、Scoped Value 与上下文传递

Scoped Value 解决什么问题?

Scoped Value 用于在线程及其子任务范围内安全传递只读上下文,例如 Trace ID、租户信息。

与 ThreadLocal 相比,它强调:

  • 值不可变。
  • 生命周期有明确词法范围。
  • 更适合虚拟线程和结构化任务。

版本状态:Scoped Values 在 JDK 25 定稿(JEP 506);JDK 21~24 上是预览特性。生产使用以项目 JDK 版本为准,21 上不要把预览能力当稳定 API 依赖。


六、现代 Java 工程实践

Optional 应该如何正确使用?

  • 适合作为“可能没有结果”的返回值。
  • 不建议作为实体字段、方法参数或序列化模型的默认选择。
  • 避免无条件调用 get()
  • orElse 会立即计算默认值,昂贵操作使用 orElseGet

Stream 并行执行要注意什么?

  • 默认使用公共 ForkJoinPool,可能与其他任务互相影响。
  • 数据量小或操作简单时并行开销可能更大。
  • 避免共享可变状态和阻塞 IO。
  • 必须通过基准测试验证,不应仅凭“并行”判断更快。

模块系统 JPMS 解决什么问题?

JPMS 通过 module-info.java 声明依赖和导出包,增强封装并减少运行时缺失依赖。

大型既有项目迁移成本较高,很多 Spring 项目仍主要使用 classpath。回答时应区分 JDK 模块和 Maven 模块。


七、结构化并发

结构化并发(StructuredTaskScope)解决什么问题?

频次 ★★★★ · 难度 🟡

是什么:结构化并发(JDK 21 正式发布,JEP 453)让并发任务的生命周期绑定到一个词法作用域——子任务要么全部完成,要么全部取消,不会出现”提交了任务但忘了 join”导致的资源泄漏。

核心用法:

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Subtask<String> user = scope.fork(() -> fetchUser(id));
    Subtask<List<Order>> orders = scope.fork(() -> fetchOrders(id));
 
    scope.join();               // 等待所有子任务完成
    scope.throwIfFailed();      // 有失败则抛出第一个异常
 
    return new Result(user.get(), orders.get());
}

三个核心设计:

  1. 作用域绑定try-with-resources 控制子任务生命周期,scope 关闭时未完成的子任务自动取消
  2. 结构化父子关系:子任务继承父任务的 StructuredTaskScopeScopedValue,形成可追踪的调用树
  3. 异常短路ShutdownOnFailure 任一子任务失败就取消其余;ShutdownOnSuccess 拿到第一个成功结果就取消其余

与 CompletableFuture / ExecutorService 的对比:

维度CompletableFuture / ExecutorServiceStructuredTaskScope
任务生命周期提交后不受控,忘记 join 会泄漏try-with-resources 自动关闭,离开作用域未完成自动 cancel
父子关系无,这是最根本的缺失显式父子作用域,cancel 向上传播
异常处理每个 CF 各自处理,或 get() 时逐个处理throwIfFailed() 统一短路
上下文传递ThreadLocal 需手动传递Scoped Value 自动继承
取消future.cancel() 只取消自己scope 关闭取消所有未完成子任务

常见追问:

  • 与虚拟线程是什么关系?→ 互补关系:结构化并发解决”任务编排与生命周期管理”,虚拟线程解决”高效承载大量任务”;两者常一起使用——虚拟线程做载体,StructuredTaskScope 做编排
  • ShutdownOnFailure 和 ShutdownOnSuccess 各适用什么场景?→ 前者适合”全部成功才有意义”(如查多个源聚合),后者适合”抢答式”(如多个镜像地址取最快返回)
  • 能替代 CompletableFuture 吗?→ 不能完全替代。结构化并发适合扇出(fan-out)场景,CF 的优势在链式编排(thenApply/thenCompose)和回调。复杂流水线场景两者互补

参考资料