金九银十的招聘节奏,对 Java(AI) 岗的候选人是一次阶段性压力测试。打开面试清单,Java 基础、并发编程、JVM、MySQL、Spring 几乎必考,最近还叠加了 Spring AI、大模型应用、AI Agent 这类新方向。很多候选人八股文背得熟,但面试官一追到“为什么参数这么配”“线上 OOM 怎么定位”“这个场景你会选哪种方案”,就开始发虚。这篇文章围绕 Java(AI) 岗面试高频考点,从八股文、场景题、项目面三个层面拆解,给出复习主线和回答思路,适合正在准备秋招春招、跳槽面试,或者想系统性查漏补缺的 Java 工程师。
真正通过多轮面试的人,并不是把所有题都背了一遍,而是把知识点串成了推理链。八股文是话语体系,场景题是工程判断,项目面是复盘能力。下面直接从面试官视角出发,按照考察板块、高频题目、回答框架、踩坑点和复习清单的顺序展开。
1. 金九银十 Java(AI) 岗面试,考察的到底是什么
1.1 面试官在八股文背后想验证什么
很多候选人把八股文理解为“背答案”,这是理解偏差。面试官问 HashMap 的加载因子、问 JVM 内存模型、问 Spring 三级缓存,不是真的想考你记性,而是想确认你能否在这个知识体系里进行正常沟通。八股文只是入口,紧接着的追问才是重点。
例如面试官问:“HashMap 为什么默认负载因子是 0.75?”背后链路可能是:
- 负载因子调大会怎样,调小会怎样。
- 什么时候触发扩容,扩容过程发生了什么。
- JDK 1.7 和 1.8 的扩容有什么区别。
- 多线程扩容时可能出现什么问题。
- 换成 ConcurrentHashMap 又是怎么解决的。
这条链路走下来,其实覆盖了 Java 基础、并发、数据结构设计、版本差异和线上风险。所以复习时不要只记结论,要把结论背后的推导过程准备出来。面试官想看到的回答是“因为过高会加剧哈希冲突,过低会浪费空间,0.75 是空间和时间的折中”,而不是“默认就是 0.75”。
1.2 面试考察结构与复习主线
Java(AI) 岗与传统 Java 岗相比,多了一条 AI 应用延伸线,但底层考察重点没有本质变化。通常按下面几个板块分布:
| 考察板块 | 代表题目 | 面试官真实目的 |
|---|---|---|
| Java 基础 | HashMap、equals/hashCode、泛型、异常 | 确认编码基本功 |
| 并发编程 | 线程池参数、锁、AQS、volatile | 判断并发场景经验 |
| JVM | 内存区域、GC、调优参数、OOM 排查 | 线上问题排查能力 |
| MySQL | 索引、事务、锁、慢 SQL | 数据层设计能力 |
| Spring 体系 | IoC、AOP、事务、三级缓存、Spring Boot | 框架原理掌握度 |
| AI 应用延伸 | Spring AI、RAG、Function Calling、Agent | 判断工程新方向敏感度 |
| 场景题与项目面 | 幂等、超时、限流、缓存一致性 | 综合工程判断力 |
复习主线可以按“概念 - 原理 - 场景 - 排错”四步走。先能准确说出概念,再解释底层原理,然后放到业务场景里说明怎么用,最后能讲清楚线上出错时怎么排查。四个层面都覆盖的候选人,即使遇到没有准备过的题目,也能靠推理框架撑住场面。
2. Java 基础与并发编程:最容易被追问的八股高地
2.1 HashMap 到 ConcurrentHashMap,面试官的追问链路
HashMap 是 Java 基础面试的常客,也是面试官最容易连环追问的题目。先看高频考点:
- 底层结构是数组加链表,链表过长时转红黑树。
- 默认容量是 16,默认负载因子是 0.75。
- 扩容后容量必须是 2 的幂,因为
hash & (length - 1)可以高效取模。 - 链表长度到 8 且数组长度达到 64 时转红黑树,避免极端哈希碰撞导致查询退化为 O(n)。
- JDK 1.7 扩容采用头插法,多线程扩容时可能形成循环链表;JDK 1.8 改成尾插法,缓解了这个问题,但 HashMap 本身仍然不是线程安全的。
追问到并发场景时,自然落到 ConcurrentHashMap。JDK 1.7 使用 Segment 分段锁,JDK 1.8 改为 CAS 加 synchronized 对单个桶加锁,锁粒度更细,并发度更高。还有一个常见问题:ConcurrentHashMap 的 size() 是否强一致?它返回的是一个统计快照,不是强一致值,因为并发写入下无法保证精确总数。
这里也是容易踩坑的地方。面试时只回答“HashMap 线程不安全”而不往下说,会让面试官觉得你只会背结论。推荐按“底层结构 - 参数设计 - 扩容过程 - 并发问题 - 替代方案”的顺序回答,把一个问题讲成一条完整链路。
2.2 线程池七个参数要背,更要能画出拒绝策略
线程池是并发编程的高频题。核心是先理解执行流程,再记忆参数。看一个典型创建方式:
ThreadPoolExecutor executor = new ThreadPoolExecutor( 2, // corePoolSize 核心线程数 8, // maximumPoolSize 最大线程数 60L, TimeUnit.SECONDS, // 非核心线程空闲存活时间 new ArrayBlockingQueue<>(100), // 工作队列 Executors.defaultThreadFactory(), // 线程工厂 new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略 );执行流程是:线程数小于核心线程数时,创建核心线程处理任务;核心线程已满,任务进入工作队列;队列已满,创建非核心线程;线程数达到最大线程数且队列已满,触发拒绝策略。
需要特别注意两个常见问题。第一,为什么不推荐Executors.newFixedThreadPool()?因为它内部使用无界LinkedBlockingQueue,任务无限堆积时可能内存溢出。第二,拒绝策略怎么选:
| 拒绝策略 | 行为 | 适用场景 |
|---|---|---|
| AbortPolicy | 抛出 RejectedExecutionException | 默认策略,能够快速暴露问题 |
| CallerRunsPolicy | 由调用线程执行任务 | 削峰填谷,不希望丢弃任务时使用 |
| DiscardPolicy | 直接丢弃任务 | 允许丢任务、业务可补偿时使用 |
| DiscardOldestPolicy | 丢弃队头任务 | 新任务比排队任务更值得执行时使用 |
回答线程池问题时,最好能举一个真实配置例子,说明队列长度为什么选这个值、拒绝策略为什么这么选。面试官真正关心的是你在线程池调整上有没有做过权衡,而不是单纯背书。
2.3 volitile、锁和 synchronized/ReentrantLock 容易错在哪里
volatile 是最容易被误解的关键字。它保证可见性和有序性,但不保证原子性。所以 volatile 适合状态标记位:
private volatile boolean running = true; public void stop() { running = false; } public void doWork() { while (running) { // 业务逻辑 } }如果用它做计数器,比如多个线程同时执行count++,volatile 的可见性并不能阻止读改写之间的竞争,最终结果一定小于预期。这里要区分 synchronized 和 ReentrantLock:
- synchronized 由 JVM 实现,支持锁升级,无需手动释放。
- ReentrantLock 提供可中断锁、公平锁、条件变量,需要手动 unlock,通常配 try/finally 使用。
- 两者都是可重入的,面试时不要回答成“synchronized 是可重入,ReentrantLock 不可重入”。
Java 基础部分整体复习时,建议把 equals/hashCode 约定、String 不可变性、try-with-resources、接口与抽象类的区别也过一遍。这些题不复杂,但很容易出现在一轮面试的前十分钟,答不好会直接影响印象分。
3. JVM 不是只背内存模型,要从现象推到参数
3.1 JVM 内存区域与对象分配,用一道题讲清
JVM 面试题里,内存区域属于必背,但只有把每个区域和错误现象对应起来,才算真正掌握。先看一张对照表:
| 内存区域 | 线程私有还是共享 | 典型异常 |
|---|---|---|
| 程序计数器 | 线程私有 | 无 OOM |
| Java 虚拟机栈 | 线程私有 | StackOverflowError |
| 本地方法栈 | 线程私有 | StackOverflowError |
| Java 堆 | 线程共享 | OutOfMemoryError: Java heap space |
| 方法区 / 元空间 | 线程共享 | OutOfMemoryError: Metaspace |
面试官常给一道题:一个对象从创建到回收,经历了哪些内存区域?回答思路是:对象创建后先进入堆,如果开启逃逸分析且对象未逃逸,可能栈上分配;多线程场景下对象优先在 TLAB(线程本地分配缓冲区)分配,避免竞争;大对象直接进入老年代;年轻代对象经过多次 Minor GC 后晋升老年代;最后通过 GC 回收。
这里不要漏了两个关键点。第一,-Xmx设置的是堆最大内存,而-Xms是堆初始大小,生产环境通常会设为相同值,避免运行时频繁扩容缩容。第二,栈溢出不一定只有递归死循环,超大本地变量表或深层方法调用也会触发,排查时用jstack看线程栈更容易定位。
3.2 GC 收集器选型:从 Serial 到 G1
GC 部分先讲清楚可达性分析。GC Roots 通常包括局部变量、静态字段、JNI 引用等,从这些根节点出发遍历对象图,不可达的对象判定为可回收。引用计数法因为循环引用问题,主流 JVM 不采用。
收集器方面,比较重要的是 G1。G1 把堆划分为多个 Region,维护 Remembered Set 来记录跨 Region 引用,垃圾回收时不需要全堆扫描。G1 的特点是可以设置期望停顿时间,但它是一个软约束,不能保证完全精确。面试常问 G1 和 CMS 的区别:
| 对比点 | CMS | G1 |
|---|---|---|
| 堆结构 | 传统分代连续内存 | 逻辑分代,物理上按 Region 管理 |
| 并发阶段 | 并发标记、并发清理 | 并发标记、混合回收 |
| 碎片问题 | 可能产生内存碎片 | 通过复制整理减少碎片 |
| 停顿控制 | 不可控 | 可设置期望停顿时间 |
| 适用场景 | 低延迟、中小堆 | 大堆、低延迟、可控停顿 |
需要说明的是,JDK 版本不同,默认收集器也不同。面试这样说更稳妥:不同 JDK 版本默认采用的收集器并不一致,生产环境以自己项目实际使用的 JDK 版本为准。
3.3 高频 JVM 调优参数与 OOM 排查路径
JVM 调优参数是场景题常考点,也是很多人只背不实践的部分。看一组常用启动参数:
java -Xms2048m -Xmx2048m \ -XX:MaxMetaspaceSize=512m \ -XX:+HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath=/data/logs/app.hprof \ -XX:CompileThreshold=10000 \ -jar app.jar参数含义如下:
-Xms/-Xmx:堆初始值和最大值,生产环境建议设为一致。-XX:MaxMetaspaceSize:限制元空间大小,避免类加载过多时无限增长。-XX:+HeapDumpOnOutOfMemoryError:OOM 时自动导出堆快照。-XX:HeapDumpPath:堆快照保存路径。-XX:CompileThreshold:方法调用多少次后触发 JIT 编译。这个参数的默认值在不同 JDK 版本中不同,不要照搬数值,面试时说清楚“它是 JIT 编译阈值,具体默认值和版本相关”即可。
OOM 排查路径建议按照这个顺序:
- 先看完整异常类型,是
Java heap space、Metaspace还是Direct buffer memory。 - 确认发生时间点和对应的业务动作,判断是流量突增还是内存泄漏。
- 如果配置了 HeapDump,拿到 hprof 文件用 MAT 分析,看大对象和支配树。
- 同时用
jstack查看线程栈,确认是否有线程阻塞或死锁。 - 线上执行
jmap需要谨慎,因为会触发 STW,高峰期操作前要评估影响。
jmap -dump:format=b,file=/data/logs/app.hprof <pid> jstack <pid>注意:JVM 调优不是只调参数。先看 GC 日志、堆使用趋势和业务流量,再决定是调大堆、改收集器,还是优化代码里的对象创建频率。
4. MySQL 从索引到事务,场景题必须落在 SQL 上
4.1 索引失效场景与索引设计原则
MySQL 面试题里,索引是被追问最深的部分。先理解为什么 InnoDB 用 B+ 树:叶子节点保存数据,非叶子节点只存索引值,树的高度低,磁盘 IO 次数少;叶子节点之间通过双向链表连接,范围查询高效。
联合索引需要掌握最左前缀原则。假设表上有联合索引(name, age, city),查询条件如下:
| 查询条件 | 索引命中情况 | 说明 |
|---|---|---|
| WHERE name = '张三' AND age = 25 | 命中联合索引 | 从最左列开始 |
| WHERE name = '张三' | 命中联合索引 | 覆盖了最左列即可 |
| WHERE age = 25 | 无法命中该联合索引 | 缺少最左列 name |
| WHERE name = '张三' AND city = '上海' | 部分命中 | 只能用到 name 列,city 无法继续匹配 |
索引失效的常见原因包括:对索引列使用函数或计算、隐式类型转换、LIKE '%xx'、用OR连接非索引列。例如下面这条 SQL,即使create_time上有索引也会失效:
SELECT * FROM order_table WHERE DATE(create_time) = '2024-06-01';推荐改写为范围查询,才能利用索引:
SELECT * FROM order_table WHERE create_time >= '2024-06-01 00:00:00' AND create_time < '2024-06-02 00:00:00';索引设计原则里,有两个容易忽略的点。第一,区分度高的列适合做索引,性别这类低区分度字段单独建索引意义不大。第二,覆盖索引可以减少回表,但不要为了覆盖而把所有查询字段都塞进索引,索引列过多会降低写入性能,并增加存储成本。
4.2 事务隔离级别与锁,怎么讲才不背盘
事务部分先明确 ACID,但更重要的是隔离级别和锁机制。MySQL 默认隔离级别是 REPEATABLE READ,主要通过 MVCC 实现快照读,通过锁实现当前读。面试官经常追问:REPEATABLE READ 为什么能解决幻读?
答案可以这样组织:
- MVCC 通过
undo log和read view提供一致性快照,普通SELECT是快照读,读到的是某个事务开始时的数据视图。 - 当前读如
SELECT ... FOR UPDATE、UPDATE、DELETE会使用临键锁(行锁加间隙锁),锁定范围,防止其他事务在这个范围插入新记录。 - 默认隔离级别下的快照读能规避大部分幻读,但不能完全等同于串行化。
库存扣减是场景题的经典例子。正确写法要利用条件判断防止超卖:
UPDATE sku_stock SET stock = stock - 1 WHERE sku_id = ? AND stock > 0;这个方案判断返回的影响行数:如果等于 1,说明扣减成功;如果等于 0,说明库存不足。相比先查询再更新,它在单条 SQL 内完成原子操作,避免了并发下的竞态问题。但要注意,这只是单库单表场景的简单方案,分布式环境还要结合分布式锁或乐观锁。
4.3 慢 SQL 排查与 EXPLAIN 使用
慢 SQL 是生产环境最常见的性能问题之一,面试必问。排查链路是:
- 确认慢查询日志是否开启,以及慢查询阈值。
- 拿到慢 SQL 后,先看表结构和数据量。
- 用
EXPLAIN查看执行计划。 - 根据
type、key、rows、Extra判断是否需要加索引或改写 SQL。
EXPLAIN SELECT id, order_no, amount FROM order_table WHERE user_id = 123 ORDER BY create_time DESC LIMIT 10;EXPLAIN 结果中,关注这几个字段:
| 字段 | 含义 | 需要关注的点 |
|---|---|---|
| type | 访问类型 | 从 system 到 const、ref、range、index、ALL,性能依次变差 |
| key | 实际使用的索引 | 如果为 NULL,说明没有用索引 |
| rows | 预估扫描行数 | 越小越好 |
| Extra | 附加信息 | 出现 Using filesort、Using temporary 时通常需要优化 |
如果Extra出现Using filesort,说明排序没有利用索引。可以在 ORDER BY 字段上设计联合索引,让排序直接走索引,避免临时文件排序。
候选人在回答慢 SQL 优化时,容易一上来就说“加索引”。正确顺序是:先看执行计划,确认瓶颈是扫描行数、排序,还是Join方式。如果查询本身需要扫描全表才能得到结果,加索引可能只是解决表象。
5. Spring 全家桶:从三级缓存到 Spring AI
5.1 Spring IoC 与三级缓存,为什么需要多级
Spring 面试题中,“三级缓存”是一个典型的深度考点。先看三个缓存的作用:
| 缓存 | 名称 | 存储内容 |
|---|---|---|
| 一级缓存 | singletonObjects | 完整单例 Bean |
| 二级缓存 | earlySingletonObjects | 提前暴露的 Bean,可能未完成属性填充 |
| 三级缓存 | singletonFactories | 存放 ObjectFactory,用于生成代理或原始引用 |
面试官会问:为什么不能用一级缓存直接存完整对象?因为 Spring 处理循环依赖时,A 依赖 B,B 依赖 A,如果等 A 创建完成再暴露,B 就找不到 A。所以 Spring 在 A 实例化后,先把 A 的 ObjectFactory 放入三级缓存,提前暴露引用。B 填充属性时拿到 A 的早期引用,等 B 完成后,A 再从三级缓存升级到一级缓存,完成自己的初始化。
更进一步的追问是:一级缓存不够,二级缓存为什么还要三级缓存?答案是:Spring 创建 Bean 时,如果目标对象需要 AOP 代理,代理对象通常在初始化后生成,但循环依赖要求早期引用也是代理,否则 A 注入给 B 的是普通对象,最终 A 完成时又是一个代理对象,两个对象不一致。三级缓存里的 ObjectFactory 可以延迟生成代理,保证引用结束前拿到正确的代理对象。
这里有一个高频坑:构造器注入的循环依赖无法被三级缓存解决,因为构造器要求在实例化阶段就拿到依赖,而 Spring 此时还没有把早期引用放入缓存。
5.2 事务失效场景与常见坑
Spring 事务失效是场景题的常客。先看一个典型错误:
@Service public class OrderService { @Transactional public void createOrder(Order order) { this.updateStock(order); this.saveOrder(order); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void updateStock(Order order) { // 扣减库存 } }这段代码的问题在于:this.updateStock(order)调用的是当前对象的原始方法,不是 Spring 容器中的代理对象。REQUIRES_NEW是事务传播行为,要生效必须通过代理调用。同一个类内部调用时,Spring AOP 拦截不到,事务不会按预期独立开启。
常用失效场景整理如下:
| 场景 | 原因 | 处理方式 |
|---|---|---|
| 同类内部方法调用 | 没有经过代理对象 | 拆到另一个 Service,或获取代理对象调用 |
| 异常被 catch 后没有抛出 | 事务拦截器观察不到异常 | 抛出异常,或使用事务边界回滚回滚 |
| 抛出的是受检异常且未配置 rollbackFor | Spring 默认只回滚 RuntimeException | rollbackFor = Exception.class |
| 方法不是 public | Spring AOP 默认对 public 方法生效 | 改成 public |
| 数据库引擎不支持事务 | 比如 MyISAM | 改用 InnoDB |
排查事务不生效时,不要只盯注解,先确认调用链路上是否经过了代理。如果是在同一个类里调用的,无论如何改propagation都没有意义。
5.3 Spring AI 与 AI Agent 面试考察趋势
AI 相关热点已经进入 Java 岗位面试。Spring AI 这类项目解决的是一个实际问题:让 Java 应用能够用统一的方式接入大模型,像 Spring Boot 管理数据库、消息队列一样管理 AI 能力。
面试中可能问到这些方向:
- 在 Java 应用中怎么接入大模型接口。
- 如何让模型回答私域知识。
- Function Calling 如何让模型触发 Java 方法。
- AI Agent 是什么,和普通接口调用有什么不同。
回答思路不要停留在“调用 HTTP 接口”层面,要往工程落地靠。私域知识可以讲 RAG:文档切分、向量化、存入向量数据库、查询时召回相关内容并拼进 Prompt,减少大模型幻觉。Function Calling 可以让模型输出结构化函数调用参数,Java 侧解析后执行真实业务逻辑。
AI Agent 的核心概念可以这样解释:Agent 不只是回答问题的聊天接口,它能在一次任务中根据模型决策,组合调用多个工具,循环执行“观察 - 决策 - 行动”,直到完成目标。Java 工程师的价值在于把 Agent 行为约束到业务系统内,控制权限、超时、日志和异常兜底,而不是让模型随意操作系统资源。
6. 场景题与项目面:拉分的关键战场
6.1 场景题答题框架
场景题是 Java(AI) 岗面试最容易拉开差距的部分。常见的错误回答方式是:面试官问“订单超时怎么处理”,候选人直接说“用延迟队列”。这个回答缺少两个关键环节:约束确认和方案取舍。
推荐按固定框架回答:
- 确认约束:业务数据量有多大,允许的延迟是多少,一致性要求有多高。
- 拆解核心问题:是超时关单、幂等防重、还是库存扣减。
- 给出主方案和实现要点。
- 说明方案的代价和取舍。
- 补充降级方案和监控手段。
比如订单超时取消,不能只回答一个技术组件,要按不同量级选择方案。
| 方案 | 实现思路 | 适用场景 | 缺点 |
|---|---|---|---|
| 定时任务扫描 | 每隔一段时间扫描超时订单,改状态 | 订单量小、秒级延迟不敏感 | 延迟受轮询间隔影响,数据量大时压力大 |
| 延迟队列 | 订单创建时放入延迟队列,到期消费 | 需要准实时处理 | 需要配套消息队列,要考虑重试与持久化 |
| 时间轮 | 在 JVM 内存中管理定时任务 | 超时任务量大、短延迟 | 进程重启丢状态,需要结合数据库兜底 |
回答时,把主方案说清楚,同时补充一个兜底:不管用哪种方案,最终状态以数据库为准,通过定时任务扫描来修正不一致记录。这个“兜底”意识是面试官比较看重的工程思维。
注意:场景题没有唯一正确答案。面试官在听你回答时,重点是你有没有考虑并发、失败、重复执行和降级,而不是你使用了多高深的技术。
6.2 幂等、限流、缓存一致性等经典场景思路
幂等性在支付、下单、消息处理场景非常常见。保证幂等的手段之一是唯一业务键加数据库唯一约束:
INSERT INTO idempotent_record (biz_key, status) VALUES (?, 'PROCESSING');如果 insert 成功,说明这个请求没有被处理过,继续执行业务;如果遇到唯一键冲突,说明请求重复,直接返回或进入已处理分支。这里的核心是:幂等不能依赖“先查再更新”,要依靠数据库约束从源头挡住并发重复请求。
限流方面,常用令牌桶算法。单机可以通过 Guava RateLimiter,分布式场景推荐 Redis Lua 脚本,保证检查和扣减行为的原子性。回答时要说明限流维度:按用户、按 IP、按接口,以及超过阈值后是拒绝还是排队。
缓存一致性是另一个高频场景。Cache Aside 模式是大多数业务的首选:先更新数据库,再删除缓存。为什么不是先删缓存再更新数据库?因为删缓存后、更新数据库前,如果有并发读请求,可能把旧数据读进缓存。这里还要提延迟双删:更新数据库后删除缓存,延迟几百毫秒再次删除,减少并发窗口。这个方案不是强一致方案,但可以降低不一致概率,成本较低。
6.3 项目面怎么讲才能让面试官追问你会的
项目面最怕两种表现:一种是只讲业务功能,不讲技术难点;另一种是罗列了一堆技术名词,但说不清为什么选它。推荐按照“背景 - 难点 - 方案 - 验证 - 踩坑”的结构表达。
比如你做过一个下单服务,可以这样组织:
- 背景:订单峰值 QPS 高,库存扣减需要精确。
- 难点:并发扣库存可能超卖,订单状态可能不一致。
- 方案:数据库条件更新加库存校验,Redis 先做热点预扣减,异步任务核对最终状态。
- 验证:压测确认 TPS,利用日志和监控核对补偿任务执行情况。
- 踩坑:最初只依赖 Redis 扣减,Redis 宕机后无法恢复,后来改为数据库最终校验。
面试官听完这段故事,会顺着你的方案追问分布式一致性、Redis 持久化、异步任务可靠性。这些方向你应该提前准备好。项目经历不是越多越好,而是要把每一段的“决策点”讲清楚。讲项目时最忌讳背流程,变成“我用了 Redis、用了 MQ、用了微服务”这种名词堆砌。
7. 十面九过的人,在面试中做对了什么
7.1 错误示范:背答案 vs 讲推理
十面九过的人,不是把所有面试题都押中了,而是在回答方式上更接近工程师思维。看一组对比:
| 面试官提问 | 错误回答 | 推荐回答 |
|---|---|---|
| volatile 保证什么? | volatile 保证可见性和有序性 | 它保证可见性和有序性,但不保证原子性,所以适合状态标记,不适合计数器 |
| 线程池怎么配置? | 背出七个参数 | 根据业务区分核心线程和队列,并说明拒绝策略的选择依据 |
| 事务为什么失效? | 因为 catch 了异常 | 同一类内部调用没走代理对象,或者异常类型不在回滚范围内,并给出验证方法 |
区别点在于:背答案的人只会给出定义,讲推理的人会继续说出边界条件和工程启示。后者更有价值,因为面试官能顺着你的回答继续提问,如果你能接住,面试就会变成讨论,而不是审问。
7.2 一个可能的面试回答范例
以“如何设计限流方案”为例,推荐的组织方式:
“我会先确认目标的 QPS 和允许的突刺。如果业务集中在单机,直接用 Guava 的 RateLimiter 令牌桶就够了;如果多实例部署,需要做一个中心化的限流,我倾向用 Redis 加 Lua 脚本保证扣减令牌的原子性。这里有一个取舍:Redis 限流会增加一次网络开销,限流器本身也可能成为瓶颈,所以我会给限流器加降级开关,Redis 异常时降级为本地限流,保证核心链路可用。同时每个接口的限流阈值分开配置,防止一个接口打爆另一个接口。”
这段回答的优点是:先说约束,再说方案,再说取舍,最后给降级。整个过程没有依赖某个“标准答案”,而是展示了工程决策能力。
7.3 面试节奏与复盘方法
面试过程中,掌握一个回答原则:先结论后展开。比如被问到 OOM 排查,先快速说“我通常先看异常类型,再确认是堆、元空间还是直接内存的问题,然后结合 heap dump 和线程栈定位”,之后再展开细节。如果一开始就扎进具体参数,面试官反而抓不住重点。
面试后的复盘要有记录。建议准备一张复习表,每天面试结束后把问题按知识点归类,标记出哪些题真正答上来了,哪些题是听到题目后头脑空白。对于没答上来的题,不要只看答案,要自己写代码复现或用文字复述一遍推理过程。复盘的价值是发现知识盲区,而不是收集更多题目。
8. 常见坑与复习清单
8.1 Java(AI) 岗面试最常见的三个坑
第一个坑:只背八股,没有代码验证。HashMap 的树化条件、线程池的拒绝流程、事务失效场景,这些内容不写代码很难真正理解。哪怕每天只写一个小 Demo,也比纯刷题有效。
第二个坑:答原理时缺少“为什么”的层次。回答“负载因子是 0.75”只是现象层,能说出这个值是空间和时间的折中,才算进入原理层。面试官连续追问时,能不能再往上一层,取决于平时有没有追问自己的习惯。
第三个坑:场景题只给方案,不聊约束和取舍。比如“缓存一致性用延迟双删”,如果不说为什么需要延迟、延迟多久、失败后如何兜底,面试官会认为你只是背过一篇文章,没有真实落地经验。
8.2 考前一周复习清单
考前不要漫无目的地刷题,按板块建立完成标准更高效。参考清单如下:
| 阶段 | 复习内容 | 完成标准 |
|---|---|---|
| Java 基础与并发 | HashMap、ConcurrentHashMap、线程池、锁 | 能写出线程池创建代码,并解释拒绝策略选择 |
| JVM | 内存区域、GC、常见收集器、调优参数 | 能说清 OOM 排查路径,能解释 G1 的 Region 设计 |
| MySQL | 索引、事务隔离级别、锁、慢 SQL | 能对慢 SQL 做 EXPLAIN,并根据结果给出优化 |
| Spring | IoC、AOP、事务、三级缓存 | 能画出三级缓存处理循环依赖的过程 |
| AI 延伸 | Spring AI、RAG、Function Calling、Agent | 能说出一个最小 AI 应用集成流程 |
| 场景题 | 幂等、超时、限流、缓存一致性、分布式事务 | 每个场景都能说出主方案、取舍和降级方案 |
每个体系在复习完成后,可以用“二十分钟讲清楚一个知识点”作为自测方式。比如给自己限定时间,把 Spring 三级缓存从为什么需要讲到三个缓存分别存什么,再讲代理对象怎么处理。能连续讲二十分钟不自相矛盾,这个知识点才算过关。
8.3 金九银十的复习重心:从“背得多”转向“想得清楚”
金九银十只是投递和面试频率更高的窗口,面试难度并不会因此降低。对 Java(AI) 岗来说,核心能力仍然是 Java 基础、JVM、MySQL、Spring 这些体系的深度,以及面对新方向时能否快速迁移已有经验。Spring AI、RAG、Agent 这些新概念会不断出现,但面试官真正评估的,是你能否用工程思维拆解陌生问题。
准备面试时,少刷“标准答案”,多练“推理链条”。每遇到一道题,都把它拆成“概念是什么、底层原理是什么、业务场景怎么用、线上问题怎么排查”四层。当你能把每个考点沿着这条链讲清楚,面试官的连续追问就不再是压力源,而是证明你理解深度的机会。十面九过不是靠运气,是靠准备充分之后产生的稳定输出。