
1. 为什么你的Java面试复习总是“无效努力”1.1 短期冲刺和长线学习的本质区别很多同学复习Java面试最大的问题不是不努力而是把短期冲刺做成了长线学习。长线学习的目标是建立完整的知识体系可以从JDK源码读到Spring源码从MySQL原理读到分布式中间件整个过程从容、系统、有深度。这种学习方式当然正确但它不适合只有一两个月就要面试的候选人。短期冲刺的目标很简单在有限时间内把面试中最高频、最容易考、性价比最高的知识点拿下来形成“能说、能写、能扛追问”的战斗力。所以短期冲刺最忌讳三件事按部就班从《Java核心技术》第一章开始读到最后一章。把大量时间花在冷门知识、底层源码细节和项目非要实现不可的边角功能上。只背答案不做场景化表达训练。所谓“邪修版”路线并不是让你走捷径欺骗面试官而是用更接近面试真实考察方式的方法去做复习。面试官不会问你会不会背HashMap而是问“HashMap在JDK 8中为什么引入红黑树”“put流程是怎样的”“什么时候触发扩容”。同样一个知识点复习角度不同结果完全不同。1.2 面试官到底在考什么面试官在技术面中本质上只考察三件事第一基础牢不牢。Java基础、集合、并发、JVM、MySQL、Spring这是绝大多数Java岗位的公共考点。基础不牢项目再亮也很难通过。第二实战经验真不真。项目是真实落地还是临时拼凑一道场景题追问就能问穿。比如Redis缓存失效、订单超时、接口幂等、慢SQL优化这些高频场景题必须有实际思考。第三沟通和解决问题的思路。遇到不会的问题时是硬编答案还是坦诚说不知道再尝试分析面对线上故障时是先看日志还是先重启这些行为方式会直接影响面试评价。所以“八股文”本身不是敌人。八股文是知识点的浓缩表达但只会背八股文、不会结合场景展开才是面试挂掉的根本原因。正确的复习方式是用八股文快速建立知识框架用场景题把八股文变成可表达、可迁移的实战能力。1.3 用“投入产出比”重新规划复习路线如果时间只有四周左右建议把复习时间按下面的比例分配复习模块占比原因Java基础 集合20%面试必考容易拿分性价比最高并发编程20%高频难度中上场景题密集JVM20%高频问题相对固定适合集中突击MySQL20%后端面试必考涉及索引、事务、SQL优化Spring / Spring Boot20%必考但重在理解不要死磕源码这五个模块几乎覆盖了Java后端面试80%以上的公共考点。先集中火力把这五个模块吃透再根据自己的简历内容补充Redis、消息队列、分布式、项目细节等个性化内容。接下来按照这个路线把每个模块的核心高频点拆开讲一遍。2. Java基础先把最容易拿分的部分拿下2.1 集合与HashMap源码是必考题Java基础模块中最值得花时间的不是语法而是集合框架尤其是HashMap。面试官问HashMap通常不是问“底层是什么”而是希望你能够说清楚JDK 7和JDK 8中HashMap的区别。put方法的完整流程。为什么链表转红黑树的阈值是8。什么情况下红黑树会退回链表。扩容机制是什么为什么扩容是2的幂次方。HashMap为什么线程不安全。先来看一个简化版的HashMap put流程描述这也是面试中推荐使用的表达框架1. 计算 key 的 hash 值通过 (n - 1) hash 定位到数组下标 2. 如果数组位置为 null直接放入新节点 3. 如果数组位置不为 null说明发生冲突判断节点类型 - 如果是链表节点遍历链表 - 如果是红黑树节点走红黑树插入逻辑 4. 遍历过程中如果 key 相同则覆盖 value 5. 插入完成后如果链表长度达到 8且数组长度达到 64转为红黑树 6. 最后判断当前 size 是否超过扩容阈值 threshold如果超过则 resize 扩容。面试中说完流程后建议主动补充一个细节链表转红黑树之前会先判断数组长度是否达到64如果没达到64会优先扩容而不是转树。这个细节很能体现对源码的理解程度。再来看一个面试中比较常问的“HashMap线程不安全”的问题。JDK 7中并发扩容可能产生环形链表导致get死循环JDK 8中虽然修复了死循环问题但put操作在并发场景下仍然可能发生数据覆盖。可以用下面这段代码引发思考MapString, String map new HashMap(); // 在多个线程中同时执行 put for (int i 0; i 10; i) { int finalI i; new Thread(() - map.put(key finalI, value finalI)).start(); }这段代码在不同环境下出现的结果可能不同部分key丢失、value被覆盖、甚至size不准确。原因在于HashMap的put、resize等操作不是原子操作多个线程同时修改内部结构时会发生数据竞争。针对这个问题的进阶回答是并发场景推荐使用ConcurrentHashMapJDK 8中ConcurrentHashMap使用CAS synchronized保证线程安全锁粒度是桶性能比JDK 7的段锁更好。2.2 String、异常、泛型这些“小知识点”别失分除了集合Java基础中还有一些看起来不难、但特别容易翻车的点。String是高频考点。面试中常见问题包括String为什么是不可变的。String、StringBuilder、StringBuffer的区别。String s new String(abc)创建了几个对象。intern方法的作用。String不可变的原因主要有三个字符串常量池的复用需要字符串内容不被修改安全性和线程安全考虑hashCode缓存需要字段值不变。下面是一个经典对比String s1 abc; String s2 abc; System.out.println(s1 s2); // trues1和s2都指向常量池中的同一个对象 String s3 new String(abc); System.out.println(s1 s3); // falses3指向堆中的对象 String s4 s3.intern(); System.out.println(s1 s4); // trueintern返回常量池中的引用异常体系也要掌握。最常见的考点是Error和Exception的区别。受检异常和非受检异常的区别。try-with-resources的使用。什么时候自定义异常。捕获异常后为什么不建议吞掉异常。泛型同样是容易问但很多人说不清楚的部分。需要掌握泛型擦除、泛型通配符、上界和下界。比如// 上界通配符可以读取不能写入 List? extends Number numbers new ArrayListInteger(); // 下界通配符可以写入读取只能按 Object 处理 List? super Integer list new ArrayListNumber();这个知识点最容易体现候选人是否真的理解Java泛型而不仅仅是会写List 。2.3 Java基础高频问题清单下面是Java基础模块建议按照优先级复习的问题清单ArrayList和LinkedList的区别扩容机制是怎样的。HashMap的put流程、扩容、红黑树转换条件。ConcurrentHashMap的实现原理。和equals的区别。hashCode和equals的约定。String不可变性及其好处。Java中四种引用类型强引用、软引用、弱引用、虚引用。深拷贝和浅拷贝的区别。接口和抽象类的选择。lambda表达式和函数式接口。Java 8之后的新特性。复习这些基础点时不要只记结论要准备一个“结论 一句原因 一个例子”的表达结构。比如面试官问“ArrayList为什么扩容是1.5倍”除了回答1.5倍之外可以补充一句shiftRightOneBit是右移一位相当于除以2这样新容量是原容量的1.5倍兼顾了空间浪费和扩容次数。3. 并发编程冲刺从八股文到场景题3.1 并发基础synchronized、volatile、Lock并发编程是Java面试的分水岭。基础题八股化进阶题场景化很多候选人在这里暴露真实水平。synchronized要重点掌握锁的对象是什么普通方法锁this静态方法锁Class对象同步代码块锁指定对象。JDK 6之后锁升级过程无锁 - 偏向锁 - 轻量级锁 - 重量级锁。synchronized和ReentrantLock的区别。volatile要掌握两条核心语义保证可见性线程修改volatile变量后会立即写入主内存并且使其他线程的缓存失效。禁止指令重排序。写一个经典的volatile可见性示例public class VolatileDemo { private static volatile boolean flag false; public static void main(String[] args) throws InterruptedException { Thread t1 new Thread(() - { int i 0; while (!flag) { i; } System.out.println(线程结束i i); }); t1.start(); Thread.sleep(1000); new Thread(() - { flag true; System.out.println(flag 已修改为 true); }).start(); } }如果flag不加volatile理论上t1线程可能一直读不到最新值导致死循环加上volatile之后flag的修改对t1线程可见程序可以正常结束。当然在真实运行中JIT优化、内存模型等因素会影响结果这个示例主要用来帮助理解可见性概念。synchronized和ReentrantLock的对比也常考对比项synchronizedReentrantLock锁的实现JVM层面基于MonitorJDK层面基于AQS是否可中断不一定lockInterruptibly可中断是否支持公平锁不支持非公平支持公平和非公平支持多个条件队列不支持newCondition创建多个是否可尝试获取锁不支持tryLock可尝试获取3.2 线程池核心参数与拒绝策略线程池是并发编程中一定会问的点。面试官一般从两个角度出题一是核心参数二是根据场景选择合适的线程池配置。线程池核心参数corePoolSize核心线程数。maximumPoolSize最大线程数。keepAliveTime非核心线程空闲存活时间。workQueue任务队列。threadFactory线程工厂。handler拒绝策略。先来看一个完整的线程池创建示例import java.util.concurrent.*; public class ThreadPoolDemo { public static void main(String[] args) { ThreadPoolExecutor executor new ThreadPoolExecutor( 2, // corePoolSize 4, // maximumPoolSize 60L, // keepAliveTime TimeUnit.SECONDS, // 时间单位 new ArrayBlockingQueue(100), // 任务队列 Executors.defaultThreadFactory(), // 线程工厂 new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略 ); for (int i 0; i 20; i) { final int taskId i; executor.execute(() - { System.out.println(Thread.currentThread().getName() 执行任务 taskId); }); } executor.shutdown(); } }面试中需要说清楚以下流程提交任务时如果当前线程数小于corePoolSize创建新线程执行如果线程数等于corePoolSize任务放入队列如果队列满了且线程数小于maximumPoolSize创建非核心线程执行如果线程数已经达到maximumPoolSize且队列也满了执行拒绝策略。四种拒绝策略分别是AbortPolicy直接抛出RejectedExecutionException。CallerRunsPolicy由提交任务的线程自己执行。DiscardPolicy直接丢弃任务但不报错。DiscardOldestPolicy丢弃队列中最旧的任务然后重新提交当前任务。这里要特别注意一个常见误区Executors.newFixedThreadPool使用的任务队列是LinkedBlockingQueue没有设置容量上限意味maximumPoolSize参数实际不会生效Executors.newCachedThreadPool的maximumPoolSize是Integer.MAX_VALUE可能导致创建大量线程。所以企业开发中更推荐直接使用ThreadPoolExecutor手动设置参数。3.3 高频并发场景题怎么答并发场景题是面试中真正拉开分数的部分。高频题目包括多个线程交替打印1到100。如何安全地修改一个共享变量。实现一个简单的生产者消费者模型。如何设计一个限流器。如何解决超卖问题。先看一个多个线程交替打印问题的参考实现。这里使用ReentrantLock配合多个Conditionimport java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class AlternatePrint { private static int num 1; private static final int LIMIT 100; private static final Lock lock new ReentrantLock(); private static final Condition condition lock.newCondition(); public static void main(String[] args) { new Thread(() - print(0), 线程1).start(); new Thread(() - print(1), 线程2).start(); } private static void print(int targetMode) { while (true) { lock.lock(); try { while (num LIMIT num % 2 ! targetMode) { condition.await(); } if (num LIMIT) { condition.signalAll(); break; } System.out.println(Thread.currentThread().getName() : num); num; condition.signalAll(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.unlock(); } } } }当然交替打印的实现方式有很多种synchronized wait/notify、CompletableFuture、Semaphore都能实现。面试时不需要写出最复杂的版本关键在于把同步机制说清楚并且解释为什么这样设计。回答并发场景题可以使用统一的框架先说线程安全问题本质上是“共享资源 非原子操作”然后给出方案最后说明方案的优缺点和适用场景。比如超卖问题先分析典型的SQL执行流程再给出乐观锁、悲观锁、Redis预扣库存等多种方案并说明各自在什么场景下更合适。4. JVM面试冲刺内存、垃圾回收与线上排查4.1 JVM内存模型复习重点JVM相关题目在面试中占比很高而且题目类型非常固定。只要把内存区域、垃圾回收、类加载、JVM参数这几块吃透得分率会很高。JVM内存区域要按线程私有和线程共享两个维度记忆线程私有的区域包括程序计数器、虚拟机栈、本地方法栈。线程共享的区域包括堆、方法区。JDK 8中永久代被元空间Metaspace替代这是高频考点。回答时可以这样表达元空间存储类的元数据信息。元空间使用本地内存默认情况下只受本机可用内存限制。字符串常量池从永久代移动到了堆中。永久代大小是固定上限容易触发OutOfMemoryError元空间默认使用本地内存配置更灵活。除此之外JVM内存模型Java Memory ModelJMM也是面试重点。JMM规定了主内存和工作内存的关系核心内容包括原子性、可见性、有序性。happens-before原则是说明有序性判断的重要工具建议至少记住以下几种情况程序顺序规则。锁规则解锁操作发生在后续加锁之前。volatile变量规则对volatile变量的写操作发生在对它的读操作之前。传递性。面试中常把JVM内存区域和JMM混淆要特别注意区分JVM内存区域是运行时数据区域的划分JMM是Java并发编程中的内存抽象模型两者不是一个概念。4.2 垃圾回收与常用收集器垃圾回收是JVM面试的核心。需要掌握对象存活判断引用计数法和可达性分析。GC Roots有哪些。垃圾回收算法标记-清除、标记-复制、标记-整理。分代收集理论。常用垃圾收集器。如何选择垃圾收集器。GC Roots常见包括虚拟机栈中引用的对象、方法区中静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI引用的对象、被synchronized持有的对象等。JDK 8默认使用的垃圾收集器是Parallel Scavenge Parallel Old但现代很多服务已经切换到G1。G1是JDK 9之后的默认收集器它最大的特点是支持可预测的停顿时间模型通过把堆划分为多个Region跟踪每个Region的回收价值优先回收价值最大的Region。如果面试官问“G1和CMS有什么区别”可以这样回答CMS基于标记-清除算法会产生内存碎片G1基于Region整体上可以避免碎片问题。CMS只在老年代回收需要配合ParNew使用G1同时管理新生代和老年代。G1提供了MaxGCPauseMillis参数可以指定期望的GC停顿时间。CMS在并发收集阶段会占用CPU资源可能导致吞吐量下降。JVM面试中经常出现OOM相关问题。下面是一个简单的JVM参数配置示例大家可以把它作为线上服务参数理解的起点java -Xms2g -Xmx2g -XX:UseG1GC -XX:MaxGCPauseMillis200 -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/data/logs/app.hprof -jar demo.jar这里的参数含义分别是-Xms和-Xmx设置堆初始大小和最大大小设置为相同值可以避免堆动态扩展。-XX:UseG1GC启用G1收集器。-XX:MaxGCPauseMillis设置期望最大GC停顿时间。-XX:HeapDumpOnOutOfMemoryError在发生OOM时自动生成堆转储文件。-XX:HeapDumpPath指定堆转储文件保存路径。4.3 JVM参数的记忆方法JVM参数非常多短期冲刺不需要全部背下来但需要掌握最常用的几类。第一类堆内存参数-Xms2g -Xmx2g -Xmn512m -XX:MetaspaceSize256m -XX:MaxMetaspaceSize256m第二类垃圾回收参数-XX:UseG1GC -XX:MaxGCPauseMillis200 -XX:ParallelGCThreads4第三类问题排查参数-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/data/logs/app.hprof -XX:PrintGCDetails -XX:PrintGCDateStamps -Xloggc:/data/logs/gc.log记忆JVM参数不要死记可以按照“内存设置、GC设置、日志设置、OOM处理”四个维度归类。面试中能说出每个参数的含义并且能根据一个具体场景给出建议就是很好的回答。例如面试官问“线上频繁Full GC你会怎么排查”回答思路是先通过jstat -gcutil查看GC频率和耗时。再通过jmap -heap查看堆内存使用情况。如果老年代持续增长则需要使用jmap -dump导出堆转储文件。使用MAT或VisualVM分析大对象、内存泄漏点。根据分析结果优化代码或者调整参数。还有一个需要提醒的坑遇到类似“Could not get JVM parameters and dynamic configurations properly”的报错时通常不是代码问题而是JVM参数格式、日志输出文件权限或者监控工具读取JVM信息失败导致的。排查时先检查参数拼写和路径权限再看监控Agent版本兼容性不必一上来就去调代码。4.4 OOM与线上排查流程OOM是线上最严重的问题之一也是面试官很喜欢的场景题。Java中常见的OOM类型包括java.lang.OutOfMemoryError: Java heap space堆空间不足。java.lang.OutOfMemoryError: Metaspace元空间不足。java.lang.OutOfMemoryError: unable to create new native thread无法创建本地线程。java.lang.OutOfMemoryError: GC overhead limit exceededGC频繁但回收效果差。每一个类型的排查思路都不同。堆空间不足时重点分析是否有大对象、内存泄漏、集合无限增长线程创建失败时重点检查是否创建了过多线程、线程池是否没有复用、操作系统线程数限制是否达到。线上排查OOM推荐按以下顺序进行确认是否由代码问题引起比如死循环、集合无限增长、连接未关闭。通过jmap生成堆转储文件。用MAT或JProfiler分析对象占用情况。定位到可疑对象后回看代码逻辑。修复问题后通过压测验证内存曲线是否平稳。排查OOM时有一个常见误区看到堆内存大就调大-Xmx。如果程序本身存在内存泄漏调大堆内存只是延迟了OOM发生时间并不能解决问题。正确思路是先定位内存泄漏再考虑参数调优。5. MySQL面试冲刺索引、事务与性能优化5.1 索引与B树MySQL是Java后端面试的必考模块其中索引、事务、锁、SQL优化是四大核心。索引相关问题要先从底层数据结构说起。MySQL InnoDB引擎使用B树作为索引结构面试中经常问“为什么不用B树、为什么不用二叉搜索树、为什么不用哈希索引”。B树的优势可以概括为数据都存储在叶子节点非叶子节点只存储索引键所以树更矮更宽减少IO次数。叶子节点之间有链表指针适合范围查询和排序。所有查询最终都会到叶子节点查询性能相对稳定。哈希索引适合等值查询但不支持范围查询。这也是MySQL默认不使用哈希索引作为主索引的原因。聚集索引和非聚集索引的区别也常考。InnoDB中主键索引的叶子节点存储整行数据这就是聚集索引非主键索引的叶子节点存储主键值需要回表查询。面试中经常出现一个经典问题为什么建表时建议使用自增整型主键可以从两个角度回答从B树结构看自增主键插入时只需要顺序追加不容易触发页分裂从存储空间看整型主键占用空间小非主键索引的叶子节点存储主键值也更节省空间。5.2 事务、隔离级别与MVCC事务的ACID特性是必须背熟的基础。但面试官更看重的是你能不能解释隔离级别和MVCC。MySQL InnoDB事务隔离级别有四种READ UNCOMMITTED读未提交可能产生脏读。READ COMMITTED读已提交解决脏读但可能产生不可重复读。REPEATABLE READ可重复读MySQL默认级别解决不可重复读但可能产生幻读。SERIALIZABLE串行化性能最低通过锁或MVCC基本避免并发问题。需要说明的是InnoDB在REPEATABLE READ级别下通过Next-Key Lock机制可以在一定程度上解决幻读问题这也是MySQL默认使用REPEATABLE READ的原因。MVCC的实现原理主要依赖隐藏字段、undo log和ReadView。隐藏字段包括事务ID、回滚指针、行ID等。undo log用于保存数据的历史版本。ReadView用于判断当前事务能看到哪个版本的数据。回答MVCC时可以这样总结MVCC是一种乐观并发控制手段通过多版本链和一致性读让读操作不被写操作阻塞在无锁的情况下提升并发度。5.3 锁机制与死锁MySQL的锁机制是面试高频题也是最容易混淆的部分。InnoDB锁可以按粒度分为行锁、表锁、间隙锁、Next-Key Lock。按模式分为共享锁S锁和排他锁X锁。间隙锁是只在REPEATABLE READ级别下才生效的锁主要用来解决幻读。它的原理是锁住一个范围内的间隙阻止其他事务在这个范围内插入新记录。死锁是实际开发中经常会遇到的问题。面试中常见的复现方式是两个事务各自持有一把锁同时等待对方持有的锁形成循环等待。举一个典型的死锁场景-- 事务A START TRANSACTION; SELECT * FROM user WHERE id 1 FOR UPDATE; UPDATE user SET name a WHERE id 2; COMMIT;-- 事务B START TRANSACTION; SELECT * FROM user WHERE id 2 FOR UPDATE; UPDATE user SET name b WHERE id 1; COMMIT;如果事务A先锁住id1事务B先锁住id2然后事务A等待id2的锁事务B等待id1的锁就产生了死锁。解决死锁的常用思路包括所有事务按照相同顺序访问资源。缩短事务执行时间减少持有锁的时间。合理设计索引尽量让事务访问的数据范围更小。设置innodb_lock_wait_timeout参数控制等待时间。使用死锁检测机制让代价较小的事务回滚。5.4 SQL优化与慢SQL排查SQL优化是实践性很强的部分面试中经常给出一条慢SQL让候选人分析。下面用一个例子走一遍排查流程。假设有以下表结构和查询CREATE TABLE user_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, order_no VARCHAR(64) NOT NULL, amount DECIMAL(10,2), status TINYINT, create_time DATETIME, KEY idx_user_id (user_id) ) ENGINEInnoDB;一个慢SQLSELECT * FROM user_order WHERE create_time 2024-01-01 ORDER BY create_time DESC LIMIT 10;这条SQL在create_time字段没有索引的情况下会对user_order表进行全表扫描然后再排序数据量大时性能较差。优化方案是给create_time字段添加索引ALTER TABLE user_order ADD INDEX idx_create_time (create_time);但在真实开发中优化SQL不能只凭猜测。正确流程是先使用EXPLAIN分析执行计划EXPLAIN SELECT * FROM user_order WHERE create_time 2024-01-01 ORDER BY create_time DESC LIMIT 10;EXPLAIN的返回结果中重点看以下几个字段type全表扫描是ALL使用主键是eq_ref使用非唯一索引是ref使用索引范围查询是range。一般来说type能达到range或ref说明索引使用情况比较好。key实际使用的索引名。rows估算的需要扫描的行数。Extra是否出现Using filesort、Using temporary等提示。如果Extra中出现了Using filesort说明排序没有走索引需要优化。慢SQL优化的大致思路是先通过慢查询日志定位具体的慢SQL。用EXPLAIN分析执行计划确认是否走了索引。确认是索引缺失、SQL写法问题、数据量过大还是返回列过多。考虑添加索引、改写SQL、拆分查询、分页优化等方案。对高频超大表还需要考虑分表分库或归档历史数据。5.5 MySQL高频场景题MySQL模块的高频场景题整理如下为什么索引能提高查询速度但会降低写入速度。最左前缀原则是什么为什么必须遵守。覆盖索引和回表查询的区别。为什么会发生索引失效列举几种常见情况。如何在保证安全的前提下处理大表DDL。如何优化分页深度比较大的查询。如何排查一条SQL为什么不走索引。int(5)和int(11)有什么区别。关于int(5)很多初学者会误解长度含义。int(5)并不是限制整数最大为5位它只影响显示宽度并不影响存储范围。int占4个字节存储范围是-2147483648到2147483647。zerofill属性配合显示宽度才会在不足位数时补零。这个点虽然小但面试中容易被问住。索引失效也是高频考点常见场景包括-- 对索引列使用函数 SELECT * FROM user WHERE DATE(create_time) 2024-01-01; -- 隐式类型转换 SELECT * FROM user WHERE mobile 13800138000; -- 使用OR连接非索引条件 SELECT * FROM user WHERE id 1 OR name zhangsan; -- 左模糊查询 SELECT * FROM user WHERE name LIKE %zhang%; -- 违反最左前缀原则跳过第一个索引列 SELECT * FROM user_order WHERE user_id 1 AND status 1;这些场景不需要死记理解索引树的结构就能推断出来对索引列做函数运算会导致无法使用B树的顺序查找类型转换会让优化器无法直接使用索引列本身的值进行匹配左模糊查询无法确定前缀所以B树无法快速定位。6. Spring面试冲刺IOC、AOP与事务6.1 IOC与Bean生命周期Spring是Java后端开发绕不开的框架面试中Spring IOC、AOP、事务、Spring Boot是四个核心维度。IOCInversion of Control控制反转要回答清楚两个点对象的管理权从程序代码转移到了Spring容器依赖注入是IOC的一种实现方式。Bean的生命周期是高频题。推荐这样回答解析BeanDefinition实例化Bean。属性填充注入依赖。执行Aware接口方法如BeanNameAware、BeanFactoryAware。执行BeanPostProcessor的postProcessBeforeInitialization方法。执行初始化方法如PostConstruct、InitializingBean的afterPropertiesSet、自定义init-method。执行BeanPostProcessor的postProcessAfterInitialization方法。Bean准备就绪被正常使用。容器关闭时执行销毁方法如PreDestroy、DisposableBean的destroy方法。如果觉得记不住完整顺序可以先记住一个精简框架实例化 - 属性填充 - 初始化前 - 初始化 - 初始化后 - 使用 - 销毁。然后在此基础上补充Aware接口和BeanPostProcessor。6.2 三级缓存与循环依赖Spring的循环依赖问题非常经典。面试官一般会问Spring如何解决构造器循环依赖默认情况下解决不了构造函数注入的循环依赖只能解决setter注入和属性注入的循环依赖。Spring解决循环依赖的核心机制是三级缓存。三级缓存分别是一级缓存singletonObjects存放完整的单例Bean。二级缓存earlySingletonObjects存放提前暴露的早期Bean此时还未完成属性填充。三级缓存singletonFactories存放Bean的ObjectFactory。Spring的解决思路是A创建时将自己放入三级缓存singletonFactories。A在属性填充时发现需要B于是去创建B。B创建时将B放入三级缓存。B在属性填充时发现需要A此时一级缓存中没有A二级缓存中没有A于是从三级缓存中拿到A的ObjectFactory。调用ObjectFactory的getObject方法得到一个A的早期引用放入二级缓存。B完成属性填充和初始化存入一级缓存。A继续执行从缓存中拿到B完成自己的属性填充和初始化存入一级缓存。回答这个问题的关键是要点明确三级缓存的核心目的是给BeanPostProcessor和AOP代理留出提前创建早期引用对象的机会。如果不需要AOP代理其实只用二级缓存也能解决循环依赖问题。6.3 AOP的实现原理AOPAspect Oriented Programming面向切面编程要掌握以下内容核心概念切面、切点、通知、连接点。五种通知类型前置通知、后置通知、返回通知、异常通知、环绕通知。基于注解的AOP使用方式。JDK动态代理和CGLIB代理的区别。一个简单的AOP示例Aspect Component public class LogAspect { Around(annotation(annotation)) public Object around(ProceedingJoinPoint joinPoint, OperateLog annotation) throws Throwable { long start System.currentTimeMillis(); try { return joinPoint.proceed(); } finally { long cost System.currentTimeMillis() - start; System.out.println(方法执行耗时 cost ms); } } }面试中常问JDK动态代理和CGLIB的区别JDK动态代理基于接口生成的代理类实现了目标接口。CGLIB通过继承目标类生成子类来代理。如果目标类没有实现接口Spring会使用CGLIB。Spring Boot 2.x之后默认使用CGLIB代理。6.4 Spring事务传播机制Spring事务传播机制有七种但面试中重点掌握三种就足够REQUIRED默认传播行为如果当前存在事务则加入当前事务否则新建一个事务。REQUIRES_NEW无论当前是否存在事务都新建一个事务。NESTED嵌套事务保存点机制内层事务回滚不会影响外层事务。事务失效是高频场景题常见的失效场景包括方法不是public。类内部调用即this调用。异常被捕获后没有抛出。抛出的异常类型不是RuntimeException或Error。数据库引擎不支持事务。Spring没有扫描到事务管理配置。下面是一个事务失效的典型场景Service public class OrderService { Transactional public void createOrder() { // 执行插入订单操作 updateStock(); } public void updateStock() { // 执行扣减库存操作 } }如果在同一个类中调用updateStock方法并且该方法上有Transactional注解这个事务注解并不会生效。因为Spring事务基于代理机制同类内部调用时没有经过代理对象所以事务不会生效。解决方式是拆分到不同的Service类中或者通过注入自身代理对象调用。6.5 Spring Boot与Spring AI等新方向Spring Boot在面试中更多是作为“工具”出现不是考察核心原理。需要掌握Spring Boot自动配置原理。starter机制。application.yml和application.properties的区别。profiles多环境配置。内置Tomcat的作用。Spring Boot中常用的注解。自动配置的核心是EnableAutoConfiguration注解它通过SpringFactoriesLoader加载META-INF/spring.factories文件中的自动配置类再根据条件注解ConditionalOnClass、ConditionalOnMissingBean等决定是否装配。如果面试官问到Spring AI可以这样说Spring AI是Spring生态在AI应用方向提供的集成能力核心目标是把AI模型调用抽象成类似Spring Data的编程模型降低Java项目接入大模型的门槛。相关关键词包括ChatClient、Prompt、Structured Output等。这个方向更新很快如果简历里写了一定要准备好项目细节。以Spring AI的Structured Output为例核心思路是把大模型返回的文本结构化映射为实体类public record OrderInfo(String orderId, String status, BigDecimal amount) { }使用时通过实体类类型作为输出格式让Spring AI负责解析大模型返回内容并转换为Java对象。具体API版本差异较大建议查官方文档确认不要背死记版本。7. 场景题与项目复盘八股如何变成面试分7.1 场景题的回答框架很多候选人会在场景题环节掉链子。明明八股文背得很熟练但面试官问“如果让你设计一个下单接口你怎么保证库存不超卖”就开始语无伦次。原因就是没有形成场景题回答框架。推荐使用“四步法”回答问题明确目标和约束先说出业务目标是什么当前系统的量级、并发情况、数据一致性要求。分析核心问题把大问题拆解成小问题例如流量控制、资源争抢、数据一致性、可用性。给出设计方案按照“缓存 - 异步 - 限流 - 落库 - 对账”的顺序阐述。补充风险与权衡说明方案的优缺点什么时候会失效怎么规避。例如缓存穿透问题回答可以这样组织缓存穿透是指查询一个不存在的数据由于缓存没有数据所有请求直接打到数据库。如果攻击者恶意构造大量不存在的key数据库压力会瞬间增大。解决方案有以下几种缓存空值。布隆过滤器进行前置过滤。对不存在的数据进行限流。参数合法性和幂等性校验。布隆过滤器方案的要点是把所有可能存在的数据哈希到一个很长的二进制向量中查询时先判断这个key是否一定不存在。布隆过滤器的特点是“判断不存在是准确的判断存在可能有误差”误判率可以通过调整位数组长度和哈希函数数量控制。缓存雪崩是指大量key同时过期导致大量请求直接访问数据库。解决方案包括过期时间加随机值、热点数据不过期、多级缓存、限流降级等。缓存击穿是指某个热点key在失效的瞬间大量请求同时访问数据库。解决思路是使用互斥锁只让一个线程去重建缓存其他线程等待。7.2 秒杀/分布式锁/接口幂等高频场景秒杀、分布式锁、接口幂等是后端面试出现频率最高的三个场景。秒杀系统设计题的回答要点流量控制使用消息队列削峰填谷。库存控制使用Redis预扣库存异步扣减数据库库存。接口防刷验证码、限流、IP限制。数据一致性最终一致性方案对账补偿。分布式锁的回答要点Redis分布式锁使用SET NX EX命令注意必须设置过期时间。锁的value建议使用唯一标识保证只能自己释放自己的锁。可引入Redisson看门狗机制处理过期时间问题。追求更高可靠性时考虑RedLock但也要讨论其争议和适用场景。数据库分布式锁通过唯一索引或者行锁实现性能较低。ZooKeeper分布式锁基于临时顺序节点实现可靠性高但复杂。一个简化版的Redis分布式锁实现public class RedisLock { private final StringRedisTemplate redisTemplate; public RedisLock(StringRedisTemplate redisTemplate) { this.redisTemplate redisTemplate; } public boolean tryLock(String key, String value, long expireSeconds) { Boolean success redisTemplate.opsForValue() .setIfAbsent(key, value, Duration.ofSeconds(expireSeconds)); return Boolean.TRUE.equals(success); } public void unlock(String key, String value) { String currentValue redisTemplate.opsForValue().get(key); if (value.equals(currentValue)) { redisTemplate.delete(key); } } }接口幂等性的回答要点新增接口重复提交通过唯一业务号在数据库增加唯一索引实现幂等。使用Redis分布式锁保证同一业务号只处理一次。使用数据库乐观锁通过version字段防止重复更新。使用状态机流转限制状态只能从特定状态流转到下一个状态。消费消息时通过消费记录表防止重复消费。回答场景题时不建议直接背方案最好结合自己项目中的真实场景说明取舍。没有真实项目经验的候选人可以提前做一两个仿真实战项目把关键设计细节写进简历。7.3 项目复盘四步法项目复盘是面试准备中最容易被忽视的环节。很多候选人整理了50个八股文却对自己的项目描述不完整。推荐用四步法复盘项目项目背景这个项目解决什么问题服务谁规模多大。我的职责我负责哪些模块技术选型是什么为什么这么选。核心难点你认为最复杂或最有价值的一个技术难点是什么怎么解决的。业务价值最终效果如何比如接口耗时降低、订单量提升、线上故障减少。准备项目时建议挑两个项目一个能体现技术深度的核心项目一个能体现业务理解的产品项目。每个项目写500字左右的描述并且准备好10个左右的追问问题答案。面试官经常会问“你项目中最有挑战的点是什么”这个时候不要回答“都挺常规的”要明确说出一个具体的难点然后按照“问题 - 分析 - 方案 - 结果”结构讲清楚。最好准备一个数据化的结果比如“接口TPS从200提升到800”“慢SQL从2秒优化到50毫秒”。8. 四周冲刺计划表与避坑清单8.1 四周冲刺计划如果距离面试还有四周时间可以参考下面的计划时间重点内容输出物第一周Java基础、集合、并发基础每天手写2个核心源码讲解比如HashMap put流程、线程池流程第二周JVM、MySQL整理JVM参数清单、索引优化笔记、事务隔离级别对比第三周Spring、Spring Boot、项目复盘完成项目梳理文档准备高频场景题回答第四周模拟面试、查漏补缺、简历优化每天做2轮模拟面试整理不会的问题每天的时间分配建议是上午八股文背诵和整理下午代码复现和场景题练习晚上复盘和模拟面试。面试集中冲刺阶段输入和输出要同步。只背不说是行不通的。“邪修版”的高效之处在于不需要把每一本书看完不需要把源码全部读完只需要把面试中最高频的知识点反复训练到能说能写。但是基础知识点必须真正理解不能胡编乱造。面试官只要追问两三个为什么背答案和真理解的差别就会暴露。8.2 面试前后高频问题排查表下面整理了一份面试准备和面试过程中可能遇到的问题排查表问题现象常见原因解决思路八股文背了但说不出来复习方式以看为主缺少输出改用录音回答、模拟面试等方式强迫自己说HashMap源码背了很多细节但没有主线只记知识点没有形成流程先背主流程再填充细节用“流程式记忆法”并发题目会背但场景题没思路缺少从问题到方案的转化训练先掌握四步法回答框架再复盘常见场景题项目被追问后答不上来对项目细节准备不足用四步法复盘项目并准备20个追问问题SQL优化只会加索引没有真正理解执行计划大量练习EXPLAIN把每个常见场景跑一遍JVM OOM排查不知道从哪下手缺少实战经验使用示例代码制造一次OOM完整走一遍排查流程Spring循环依赖原理说不清只记三个缓存名词用A和B创建过程画流程图并解释为什么需要三级缓存面试过程中如果遇到不会的问题不要直接沉默。可以先说“这个问题我之前没有深入研究过但我可以从几个角度分析”然后尝试分解问题。面试官更看重分析思路而不是标准答案。8.3 最后的建议短期冲刺不是投机取巧而是把时间花在真正重要的事情上。Java基础、并发、JVM、MySQL、Spring这些模块之所以反复考是因为它们是后端开发的基本功也是排查线上问题时的底层工具。从现在开始建议你先花半天时间做三件事整理一份自己的简历项目描述确保每个技术点都能回答业务为什么需要它。用录音软件模拟一次HashMap的put流程讲解训练结构化表达。找一个真实的慢SQL场景用EXPLAIN完整分析一遍。如果能把这三件事做完你的面试复习已经开始从“背八股”变成“会表达”。接下来就按照上面的路线每三天完成一个模块的集中冲刺第四周开始模拟面试。面试结果不会辜负真正认真复盘过的人。