资讯详情

2026 Java面试核心:集合、JVM、并发与Spring高频题实战解析

📅 2026/10/13 3:44:34 | 华诺云谱 👁 阅读
2026 Java面试核心:集合、JVM、并发与Spring高频题实战解析
2026年Java面试题汇总网上到处都在传“八股文没用了”但真到了面试现场你会发现问的还是那些底层原理——只是问法越来越刁钻。不再问“HashMap的原理是什么”而是问“HashMap在并发扩容时会不会形成环JDK 8之后为什么不会”不再问“线程池的几个参数”而是直接在白板上扔一段生产代码问你队列满了以后会发生什么。这是我整理的第一期围绕Java基础集合、JVM、并发编程、Spring框架四个大方向展开每个问题都附上答题思路和追问备选。后续系列还会继续补充MySQL、Redis、消息队列、分布式等模块争取做成一份可以长期迭代的面试知识库。我写这份汇总的初衷很简单面试准备最怕的就是“看了很多题但不知道背的重点在哪”。所以我不只列问题和标准答案还会把面试官真正想听到的层次讲清楚——哪些是基础分哪些是加分项哪些是答不好直接挂的致命点。文中的题目全部来自我本人以及身边同事近两年在一线互联网公司和中小厂的真实面试场景覆盖面偏向2025-2026年的出题趋势建议配合源码一起看不建议死记硬背。1. 基础集合框架HashMap是永远绕不开的C位集合类可以说是Java面试的入场券基本没人能跳过。而集合里面HashMap的出场率高得离谱几乎每一轮技术面都会涉及。高频程度肉眼可见HashMap、ConcurrentHashMap、ArrayList、LinkedList这是四个核心其中HashMap一家就占了接近一半的提问量。1.1 HashMap底层结构与扩容机制细节面试官通常从一个最简单的问法切入“说一下HashMap的底层数据结构。”很多人的回答止步于“数组加链表JDK 8之后转红黑树”这句话只能拿基础分。你需要接着往下延伸Node数组的初始长度是16负载因子默认0.75也就是说当元素个数达到12的时候触发扩容扩容之后容量翻倍到32。链表转红黑树的条件是链表长度到达8并且数组长度不小于64两个条件缺一个都不会转而是优先通过扩容来缩短链表。这里面的逻辑值得多想一步为什么树化的阈值是8而不是16或者4因为链表节点和树节点的内存占用不同——普通Node占用的内存比TreeNode小很多短链表形态下链表反而更省内存、遍历更快。只有当链表足够长树化才划算。8这个阈值在数学上遵循一个泊松分布在理想哈希算法下链表长度达到8的概率只有千万分之六所以在绝大多数场景下红黑树并不会真正登场。扩容这个话题最有挖掘空间。面试官会追问扩容发生在什么时机扩容之后元素是怎么迁移的JDK 7和JDK 8的迁移方式有什么区别这里的关键加分点是JDK 8之后不再逐个节点重新计算哈希而是通过(e.hash oldCap)判断元素属于原位置还是原位置加旧容量。原因是数组长度始终是2的n次幂扩容后新容量相当于在二进制高位多了一个1元素的索引要么不变要么增加一个旧容量值。这也是HashMap能保持高性能扩容的底层原因。再往下深挖面试官可能会抛出一个经典陷阱JDK 7的HashMap在并发环境下扩容可能形成循环链表导致get操作死循环为什么JDK 8之后解决了因为JDK 7用的是头插法多线程同时触发rehash时链表会逆序在并发迁移中可能把引用关系彻底打乱形成环。JDK 8改用尾插法元素相对位置不变从机制上消除了死循环的可能。但你如果只回答到这里面试官很可能跟一句“那JDK 8的HashMap在并发环境下就是安全的吗”正确答案是不安全虽然不会死循环了但数据覆盖、size计数不准确的问题依然存在并发场景必须用ConcurrentHashMap。1.2 ConcurrentHashMap的锁粒度演进ConcurrentHashMap的考察重点放在锁策略上。JDK 7的实现是分段锁默认16个Segment每段各自管理一组桶不同段之间可以并行写。JDK 8直接抛弃了分段锁改用CAS加synchronized锁的粒度从Segment级别细化为单个桶级别。这个演进方向非常明确锁的粒度越来越细并发度越来越高。JDK 8的put流程值得自己画一遍首先判断数组当前有没有初始化没有就通过CAS操作进行初始化注意这里的初始化会有并发竞争多个线程可能同时发现数组为空但CAS能保证只有一个线程真正执行创建动作其他线程会自旋重试。接下来计算出数组下标位置如果此时该位置是空的就用CAS直接把节点写进去这是完全没有锁的路径。如果该位置已经有节点了才会对当前桶的头节点加synchronized锁然后走链表或红黑树的写入逻辑。你会发现ConcurrentHashMap的设计哲学很有意思尽量在无竞争或极轻竞争的场景下用CAS真正发生哈希冲突时才加锁而且只锁单个桶。还有一个高频追问点是size()方法的统计方式。并发环境下直接用count计数肯定不准确所以ConcurrentHashMap引入了baseCount加CounterCell数组来分摊计数压力。每个线程先尝试CAS增加baseCount如果CAS竞争激烈失败就随机选一个CounterCell数组里的槽位去做累加。这样做的效果是即使有大量线程同时写计数器也不至于互相阻塞。1.3 字符串、ArrayList与vFail-Fast机制字符串相关的题目也是老演员了但出题角度在变化。以前喜欢问“String为什么是final的”现在更多会问“new String(abc)创建了几个对象”“StringBuffer和StringBuilder的区别底层是怎么体现的”。答前者的时候建议从安全、线程安全、字符串常量池设计三个角度说明只答“不可变是为了安全”显得太干。创建对象个数的问题本质是在考字符串常量池如果常量池里已经有“abc”new String(abc)只创建一个new出来的堆对象如果常量池里没有还要先在常量池创建一个再创建一个堆对象一共两个。引申到intern()方法时要注意JDK 7之后常量池移到堆里intern的返回逻辑也变了。ArrayList考察最多的就是“和LinkedList的区别”。常规回答是数组和双向链表的内存结构不同随机访问和插入删除性能不同。但加分答法要提到内存局部性和CPU缓存的影响ArrayList底层是连续内存空间遍历时能充分利用CPU缓存的预取机制在数据量大时随机访问性能优势明显LinkedList每个节点是散落的内存碎片还需要额外的前后指针开销。面试官比较爱设陷阱的是fail-fast机制。ArrayList在迭代过程中如果有线程修改了modCount值会抛出ConcurrentModificationException。这个机制看起来“安全”实际上并不是强一致保障底层逻辑很简单——迭代器每次获取下一个元素前都会检查expectedModCount ! modCount。追问通常是“如何避免”标准回答是用CopyOnWriteArrayList它的迭代器操作的是创建时的快照数组修改操作是在副本上进行的迭代器永远感知不到变化所以不会抛异常。2. JVM原理从内存区域到调优实战JVM知识在二面、三面中占比异常高尤其在一线互联网公司的面试中基本是必考板块。而且这个方向非常容易看出一个候选人有没有真实调优经验——你说你用过G1面试官现场追问几个参数含义和调优场景答不出来立刻穿帮。2.1 运行时内存区域划分与对象分配路径Java运行时内存区域是老生常谈但要从“Areas of JVM”升级到“对象的一生”角度回答。一个对象new出来之后绝大多数情况下先在伊甸园分配伊甸园快满了触发Minor GC存活对象移动到幸存者区S0和S1之间倒腾每熬过一次GC年龄加一默认到达15次就往老年代晋升。这里要注意大对象直接进入老年代这是由参数-XX:PretenureSizeThreshold控制的但对象太大即使设置了阈值也有可能出现直接在老年代分配的情况。内存区域之间的线程关系也是高频考点。虚拟机栈、本地方法栈、程序计数器是线程私有的堆和方法区是线程共享的。JDK 8的一个重大变化就是方法区退出历史舞台被元空间取代“永久代”这个叫法只存在于JDK 7及以前。元空间和永久代有本质区别永久代属于JVM管理的内存大小受JVM限制元空间用的是本地内存不再触发Full GC而且默认情况下大小几乎不受限制。这个设计变化带来的实际好处是很多开发者不再被PermGen Space错误折磨。还有一个常考的冷门知识点是“什么情况下会触发StackOverflowError什么情况下会触发OutOfMemoryError”。前者通常是方法递归层数过深线程栈帧把虚拟机栈挤爆了后者是堆区或元空间无法再分配内存。有些面试官会追问无限递归会导致堆内存溢出吗答案是绝大多数情况不会因为递归每层都会创建新栈帧栈先爆掉。2.2 垃圾回收算法与主流收集器对比垃圾回收相关的考点又碎又多但只要抓住一条主线就好理解了回收算法经历了从标记-清除、复制、标记-整理到分代收集的演进收集器则从Serial、Parallel发展到CMS再到今天的G1和ZGC。标记-清楚最核心的问题有两个第一是STW时间太长第二是内存碎片化频繁Full GC的场合下碎片化尤其致命。复制算法完美解决了碎片问题代价是浪费部分内存空间所以新生代幸存者区的划分就用了这个思路但复制算法在老年代不可用因为老年代存活率高复制代价太大。标记-整理算法避免了上述两个缺点但代价是移动对象需要更新所有引用过程复杂且停顿时间依然不短。收集器的选择是面试官判断你有没有真实项目经验的试金石。Parallel是JDK 8默认的收集器优势是高吞吐适合后台计算型任务CMS是追求低停顿的鼻祖但它在并发清除阶段会跑CPU而且存在“并发模式失败”一旦发生就退化为Serial Old性能一落千丈G1从JDK 9开始变成默认收集器核心思路是划分成很多大小相等的Region通过跟踪每个Region的回收价值优先回收垃圾最多的区域所以它叫Garbage First而不是全堆回收。追问G1时面试官经常问“G1的RSet是什么”这个问题很多人答不上。RSet是Region级别的记忆集记录了哪些Region引用了当前Region的对象。因为G1回收某个Region时不能只扫描该Region还得知道谁引用了它RSet就是用来记住这些外部引用的数据结构。有了RSet回收单个Region时就不用全堆扫描了这是G1能实现可预测停顿的核心机制之一。ZGC是Java 17之后开始频繁出现在高端岗位题目中的角色。它的最大特点是停顿时间控制在10毫秒以内而且堆大小不管设成几十G还是几百G停顿时间都几乎不变。原理核心是染色指针和读屏障这个实现很复杂中小公司面试一般不会深挖但如果你的岗位级别是高级工程师以上建议把ZGC和G1的对比整理清楚。2.3 类加载机制与双亲委派模型类加载的考点稳定集中在类加载的过程是什么、有哪些类加载器、双亲委派模型是什么以及为什么需要它。加载、验证、准备、解析、初始化这是JVM加载一个类的五个阶段。很多人会把“准备阶段”搞混这里专门提一句准备阶段是给类的静态变量分配内存并设置默认值不是用户代码里写的初值。比如static int a 20准备阶段a的值是0把20赋值给a发生在初始化阶段。双亲委派模型比较反直觉需要把它讲透。应用类加载器收到一个类加载请求不会自己先加载而是先交给父加载器一直向上传递到启动类加载器只有父加载器无法完成加载时子加载器才尝试自己加载。所有加载请求从下往上传递实际加载过程是从上往下进行。这个机制的核心价值是避免同一个类被加载多次保证核心类库的安全——如果开发者自己写了一个java.lang.String理论上根本不会被加载因为启动类加载器已经加载过真正的String了。面试官如果追加追问“有没有可能打破双亲委派模型”标准的切入点是线程上下文类加载器Tomcat等Web容器就是典型例子。Tomcat管理多个应用时每个应用需要隔离的类库版本如果严格执行双亲委派不同应用的同名类就会被父加载器加载后共享无法实现隔离。所以Tomcat的WebAppClassLoader会优先自己加载WEB-INF/classes下的类这就是“逆双亲委派”。另一个经典案例是SPI机制JDBC驱动加载就是通过Thread.currentThread().getContextClassLoader()来打破委派链条的。2.4 内存泄漏定位与JVM调优参数实战这部分是可以通过一道场景题来考线上应用频繁Full GC响应时间飙升你怎么排查这个问题考察的是完整的问题定位链路没有真实经验的人很容易答得支离破碎。我建议的排查路径是这样的先通过jstat -gcutil pid 1000观察GC情况确认Full GC的频率和耗时是否异常如果确认有问题用jmap -dump:formatb,fileheap.hprof pid导出堆转储文件这一步要注意生产环境大堆导出文件非常吃磁盘务必提前确认空间然后用MAT或JProfiler分析重点看对象直方图的Retained Size找大对象或者通过支配树看哪个对象持有了大量引用。最常见的泄漏场景无非那么几种静态集合类中存了对象但没有清除条件、各种连接资源没关闭、ThreadLocal里的Entry没有remove、监听器没有反注册。JVM调优参数没必要背全套但要掌握核心几组。堆大小固定的三件套-Xms -Xmx要一致防止运行时动态扩容带来的抖动新生代比例-Xmn建议设置成堆的3/8左右GC日志参数-Xloggc:/path/gc.log -XX:PrintGCDetails是排查问题的基本依赖。真正到了调优现场最常用的命令其实是jstat和jmap大部分问题靠这两个命令就能定位到八九成。3. 并发编程从线程池到AQS的完整链路并发几乎是Java高级工程师面试的“分水岭”模块。基础岗位还在问synchronized和volatile的区别高级岗位就已经开始死磕AQS和并发工具的内部实现了。3.1 线程池七个参数与执行流程源码级分析线程池最常见的问法是“说一下线程池的执行流程”网上流传的答案一般是先判断核心线程是否满了再判断队列是否满了再判断最大线程数最后拒绝。这个回答能过基础面但经过细化才能过深水区。真正的源码流程是execute方法提交任务后如果当前工作线程数小于核心线程数直接新增一个核心线程执行注意这里和前几年的旧版本逻辑有变化现在的版本是优先判断工作线程数是否达到corePoolSize没达到就新建线程。如果已经达到尝试将任务放入阻塞队列。队列成功接收任务那就等有空闲线程再去取任务执行。队列满了再判断当前线程数是否小于maximumPoolSize小于就创建救急线程临时线程执行完当前任务后会去队列里面取任务如果队列任务也被清空并且等待时间超过keepAliveTime线程就会被回收。如果线程数已经达到上限并且队列也满了才会执行拒绝策略。线程饥饿的概念也经常被顺带问起。单线程执行器是典型的例子一个单线程线程池里面如果第一个任务阻塞了后面所有任务都会排队等待这在声明“异步执行”的地方特别容易踩坑。拒绝策略有四种AbortPolicy直接抛异常这是默认的CallerRunsPolicy让提交任务的线程自己执行适合降低任务提交速度也让任务不丢失DiscardPolicy和DiscardOldestPolicy都是丢任务后者还会把队列头部的旧任务丢掉腾出空间给新任务。真实业务中最好不用默认策略不然高峰期任务一多直接抛异常上游体验非常差一般建议用CallerRunsPolicy或者自定义拒绝策略做降级处理。线程池参数怎么设置是每个面试者都要准备的场景题。CPU密集型就看核心数设核数加1到2IO密集型建议设置成核数的两倍左右更精确的做法是通过公式计算线程数 核数 * (1 等待时间/计算时间)。真实经验是IO密集型线程池可以直接用这个公式估算一个初始值再用压测微调别指望理论值一步到位。3.2 synchronized锁升级与ReentrantLock底层对比synchronized的锁升级过程是这几年热度只增不减的考点。整个过程是无锁、偏向锁、轻量级锁、重量级锁。无锁状态就是对象头里的Mark Word没有记录锁信息偏向锁的意思是同一个线程第一次获取锁后Mark Word会记录这个线程的ID下次这个线程再进来直接判断是自己的锁都不用走CAS这就是“偏向”的含义一旦出现第二个线程竞争偏向锁撤销升级为轻量级锁这个锁通过CAS操作把对象头的Mark Word复制到线程栈帧的锁记录中然后尝试把对象头修改为指向锁记录的指针如果CAS失败说明竞争激烈膨胀为重量级锁此时未获取锁的线程进入阻塞队列操作系统的互斥量参与其中。ReentrantLock和synchronized的对比题目出现频率一样高。机制上两者都支持可重入可重入的底层含义是同一把锁可以被同一个线程多次获取每获取一次lock次数加一unlock一次减一减到零才真正释放。synchronized是JVM层面的锁在对象头做文章而ReentrantLock是基于AQS框架的类实现通过state变量记录重入次数。功能上ReentrantLock多出来的能力包括可中断lockInterruptibly、可超时tryLock(TimeoutUnit)、公平锁构造参数传true、可以有多个等待条件newCondition。需要特别指出的是公平锁在真实高并发场景应用不多因为排队唤醒的开销比非公平锁大很多通常业务上对响应时间有强要求的场景才会考虑。3.3 volatile、CAS与ThreadLocal的内存泄漏真相volatile的考察核心是“可见性和有序性不保证原子性”。它通过给变量加Lock前缀指令写入时强制刷新到主内存读取时从主内存拉取最新值。另外volatile还有一个常被忽略的作用禁止重排序。双重检查锁单例模式就是利用volatile防止对象发布时指令重排带来的半初始化问题——instance new Singleton()在字节码层面有三个步骤分配内存、初始化对象、把引用赋值给静态字段。如果不加volatile第二和第三步可能被重排其他线程会拿到一个引用已赋值但内部尚未初始化的对象用起来直接空指针。CAS的三大问题必须能说全。ABA问题的含义是一个值从A改成B又改回A当前线程以为数据没有变化实际上中间被改过。Java里提供了AtomicStampedReference用版本号标识每次修改解决ABA。自旋问题在激烈竞争下CAS会一直循环失败白白浪费CPU周期分段锁和条带化机制就是为了降低竞争频率。第三是只能保证单个变量的原子操作无法作用于代码块这种场景还是得靠锁。ThreadLocal内存泄漏问题是典型的“知道原理才能解释的题目”。ThreadLocal的底层结构是一个ThreadLocalMap这个Map被Thread持有。Map的key是ThreadLocal对象用弱引用持有但value是强引用。如果线程存活且ThreadLocal对象不再被外部引用key会被GC回收但value链条依然存在这就产生了key为null的Entry——内存泄漏。正道解法是每次用完ThreadLocal后调用remove方法清除Entry这也是规范要求的标准用法比如在finally块和remove成对出现。3.4 AQS核心机制与常用并发工具AQSAbstractQueuedSynchronizer是并发包的灵魂组件。面试官喜欢直接问“AQS怎么实现的”主体结构是三大块一个volatile类型的state字段表示共享资源状态一个FIFO双向队列存放等待线程还有独占模式和共享模式两种获取方式。独占模式是lock的底层逻辑线程tryAcquire失败后会被包装成Node放入等待队列尾部然后通过LockSupport.park挂起。前面排队的线程释放锁后执行unpark唤醒后续节点。共享模式对应的是CountDownLatch、Semaphore、ReadWriteLock这些工具state的含义各不相同。比如CountDownLatch初始state为N每次countDown减一变为0时所有等待线程被唤醒Semaphore的state就是剩余的许可证数量。理解AQS之后面试官会展开追问“ReentrantReadWriteLock里面为什么可以支持锁降级但不能支持锁升级”这跟死锁风险强相关。如果有两个线程同时持有读锁都想升级为写锁但写锁需要等读锁全部释放两边相互等待就死锁了。锁降级是安全的因为持有写锁说明没有其他线程有写锁此时获取读锁再释放写锁数据一致性是能保证的。4. Spring框架高频追问生命周期与循环依赖Spring系列在Java岗位面试中的地位仅次于Java基础如果岗位职责包含业务开发那Spring的追问基本是二面重头戏。4.1 Bean生命周期完整链路与扩展点Bean的生命周期题目答得好不好往往直接决定技术面的分段位。因为这道题能反映一个开发者是不是真的读过Spring的源码。我建议的回答链路从实例化开始Spring通过推断构造方法找到候选构造器并实例化对象然后进行属性填充也就是Autowire处理Autowired和Resource这些依赖注入然后依次调用BeanPostProcessor的postProcessBeforeInitialization方法、PostConstruct注解、InitializingBean的afterPropertiesSet方法接着是自定义init-method再来是postProcessAfterInitialization方法——注意这里执行了AOP的代理逻辑在这之后Bean才具备被代理包装的能力。所有初始化完成后Bean就绪进入容器。销毁阶段会执行DisposableBean的destroy方法和自定义destroy-method。加分段位的点是扩展接口的触发时机。ApplicationContextAware和BeanNameAware在属性填充之后会触发对应回调前面还可以追问BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor的区别——前者是容器本身配置的增强器后者额外支持注册新的BeanDefinition这些都是在Bean实例化之前的阶段运行的。回答时最好从扩展点出发拉一条完整的时间线能一口气顺下来的人面试官通常会很满意。4.2 AOP底层机制JDK动态代理与CGLIBSpring AOP的考察重点集中在两种代理的选型逻辑。默认情况下如果目标类实现了接口Spring偏好使用JDK动态代理底层通过Proxy.newProxyInstance生成一个实现相同接口的代理对象核心InvocationHandler通过反射调用目标方法如果目标类没有实现接口则使用CGLIB通过生成目标类的子类覆写业务方法实现增强所以CGLIB被代理的类和方法不能是final的。Spring Boot 2.x之后AOP默认使用的是CGLIB特别是Spring Boot 2.2以后官方建议用CGLIB代理因为JDK动态代理要求目标对象必须实现接口在实际业务类越来越不依赖接口的开发模式下常出现代理类型不匹配的坑。值得追问的是Spring AOP只能作用于Spring容器管理的Bean并且只能拦截public方法因为代理对象本质上是在Bean创建时通过后置处理器包装了一层方法调用必须走代理对象才能触发拦截器链而private方法根本没机会走代理链路。切入点表达式的常见写法也建议记几个execution(public * com.example.service.*.*(..))表示匹配service包下所有类的所有public方法annotation(com.example.aspect.LogAnnotation)表示匹配带指定注解的方法。切面还可以做优先级控制通过Order注解调整多个切面的执行顺序执行顺序体现在before中最先执行、after中最晚执行。4.3 Spring事务失效的九种场景排查Transactional失效问题属于开发必问场景题答得好会迅速拉高面试官对你的实战认可度。这里想强调一点绝大多数失效原因是同一个东西——Spring事务是通过AOP代理实现的你调用的必须是代理对象的方法事务注解才会生效。具体失效场景我建议按类整理。自己类内部方法调用A方法调用同类中的B方法但B加了Transactional因为调用发生在内部没有走代理对象事务不生效解决办法是注入自身代理、拆到不同类或者用AopContext.currentProxy。方法不是public时失效因为Transactional只对public方法生效非public方法Spring默认不生成事务代理。异常被捕获处理了导致失效事务感知的是RuntimeException或者Error普通异常不触发但业务代码把异常catch住没往外抛Spring就根本感知不到。异常类型问题checked异常默认不触发回滚需要rollbackFor Exception.class。数据库引擎不支持事务MySQL的MyISAM引擎就没有事务能力等于白切。方法被final修饰CGLIB无法覆写final方法必然无法代理。传播行为设置错误比如把传播行为设为NOT_SUPPORTED当前方法会以非事务方式执行。多线程环境下事务边界在线程A创建但出错发生在子线程B子线程中的异常不影响父线程事务的回滚。4.4 循环依赖与三级缓存机制解析循环依赖在Spring中是一个既带着“为什么”又带着“怎么解决”的综合题。典型的构造器循环依赖直接无解因为实例化阶段就需要对方Spring没法解决这个问题。能被解决的是setter注入形式的循环依赖解决它的三兄弟是一级、二级、三级缓存。三级缓存的机制是解决循环依赖的钥匙要理解它最好从“非循环依赖的正常流程”看。A实例化完成后如果A依赖B先把A准备创建的实例放到三级缓存里也就是一个ObjectFactory工厂对象这个工厂的getObject方法负责返回A的早期引用——准确说是A的早期对象它可能还没有完成属性填充。当B创建时发现需要A此时A不在二级缓存但能在三级缓存中找到Spring会调用A的ObjectFactory拿到早期引用提前注入给B。B由此完成创建B的全部初始化流程结束后Spring再回头处理AA拿到B的代理对象继续完成自己的初始化。为什么说是三级缓存而不是两级这里是整道题的深水区。假设只有二级缓存所有刚实例化的Bean都直接放入二级缓存那么即使没有循环依赖也要先创建对象再放到二级缓存可是这样做会提前暴露尚未完成初始化的Bean对AOP处理的时机产生副作用。用三级缓存存储ObjectFactory的话只有在真正发生循环依赖、且需要提前注入时才会调用工厂方法创建早期引用。所以三级缓存的核心价值是“延迟了早期引用的生成”把AOP代理的时机尽可能往后推保证不破坏正常创建流程。这个点能讲明白的 собеседник的印象分会很不一样。5. 面试实战复盘高频追问与答题节奏控制讲了那么多知识点最后聊点战场上的经验。很多人技术深度过关但面试表现力不行问题就出在对追问的预判不够以及答题结构的取舍上。5.1 用STAR结构化回答源码阅读类问题源码类问题最容易答得又长又乱。面试官问“你怎么看HashMap的扩容”你从源码第一行开始背面试官很快会失去耐心。比较好用的策略是“STAR式的技术答法”先一句话给结论讲清楚这个机制的背景和目的接着提炼关键实现路径讲两三个核心步骤而不是逐行背诵再丢出一个比较深刻的细节比如“JDK 8扩容时不需要重新计算hash是通过与oldCap相与来区分高低位链表”最后补充一句这个设计在哪些场景会出问题。这样输出出来面试官既确认你真的读过源码也不会认为你在背题。应试方面还有一个大忌不懂装懂。我当时见过一个候选人被问到线程池的原理他把核心线程说成“固定数量的线程总是在跑”问他如果核心线程空闲会怎样他答“那就浪费了”细问才发现他根本没想过keepAliveTime只作用于非核心线程。这个属于知识点模棱两可死记硬背很容易被连续追问测出来。5.2 持续更新的错题本与阶段复盘方法面试题汇总类的资料很多但大部分人买回去就吃灰了。我的经验技巧是建立自己的“错题本”不是把答案抄一遍而是记录当时答不出来的切入点以及面试官追问后得到的思路补充。每道错题背后都代表一个知识盲区隔一周回头再看一次基本能形成长期记忆。另一套方法就是分阶段做复习切片。基础阶段只刷高频集合类和JVM核心题目标是能不看资料写出完整答案进阶阶段做场景化题目比如“线上频繁Full GC你怎么排查”“定时任务集群并发重复执行怎么解决”战场阶段就是做模拟追问找同事或者自己录音推演面试官可能针对每道题展开的后手。等到面试官出题目时你脑子里浮现的不再是对着文档背答案而是一张完整的技术知识图谱。6. 热身好题精选十道你未必答得全的对错题最后放一组适合用来热身和自测的题目能在短时间内检验自己的基本面。第一道一个类的实例变量在并发环境中被多个线程修改不加volatile也不加锁其他线程看到的值是否一定和最新值一致答案是不一定JMM不保证变量在多线程下的可见性。第二道Java 8的HashMap在扩容时同一个桶上的链表元素顺序是否完全保持不变答案是元素在数组中的位置会基于(e.hash oldCap)分成高低两半高位部分的顺序会保留但链表整体实际会被拆分成两条链。第三道synchronized修饰的静态方法和实例方法锁的是同一个对象还是不同对象答案是不同对象。静态方法锁的是Class对象实例方法锁的是当前实例对象。第四道ThreadLocal在并发环境下不同线程往ThreadLocal里放的值会互相覆盖吗答案是不会每条线程持有的ThreadLocalMap是线程私有的存的值是线程隔离的。第五道Spring的Transactional加在private方法上会生效吗答案是通常不生效因为事务代理无法拦截private方法而且方法本身都无法被子类覆写。第六道如果Spring Bean的构造方法里调用了另一个Bean的方法此时会发生循环依赖问题吗答案是有可能构造器阶段发生的循环依赖Spring无法解决会直接报错。第七道volatile修饰的变量能保证i的线程安全吗答案是不能i是读改写三步操作volatile只保证中间读取的值是最新的但整体复合操作不具备原子性。第八道JVM在JDK 8默认使用的垃圾收集器是哪两个答案是新生代Parallel Scavenge和老年代Parallel Old。第九道CountDownLatch和CyclicBarrier的核心区别是什么一个是门闩一次性使用计数器减到0就放行等完就没了另一个是栅栏可以循环复用所有线程到达后同时开始下一轮。第十道对一个资源操作频繁的场景是无锁CAS好还是直接加锁好答案是不一定CAS在竞争不激烈时开销低但高竞争下自旋消耗CPU此时加锁反而更稳定这也是很多中间件在极端高并发下选用分段加锁的根本原因。这十道题里如果全对说明基础框架已经足够扎实可以直接进入下一阶段刷分布式和数据库的高频题了。我自己做技术复盘时最深刻的感受是面试准备不是背会题目而是通过题目验证自己对底层机制的理解颗粒度。别再停留在“知道结论”的层面试着去解释“为什么是这个结论”这个习惯一旦养成应付2026年的Java岗位面试会轻松很多。后续的汇总也会继续往MySQL、Redis、消息队列、分布式架构方向推进欢迎在留言区补充你在真实面试中遇到的题目。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑