爪爪哇碎碎念

Java 面试 30 题

别人只给答案,这里每题都标了源码位置和追问会问什么。答不上来的,点旁边的地图节点回去补。

30 题 6 个专题 每题带出处
用这张表的方式:先遮住答案自答一遍,答不出就看「追问」——面试拉开差距的从来不是第一问, 是第二问。想深挖的点,用题后的地图链接点进去看节点。

一、JVM

  1. 堆为什么要分代? 弱分代假说:绝大多数对象朝生夕灭,少数会长期存活。分开管,才能对新生代用复制算法(便宜), 对老年代用标记整理(省空间)。 追问:G1 还分代吗?——逻辑分代,物理上是 Region,不再连续
  2. 什么对象能当 GC Roots? 栈帧里的局部变量、静态变量、常量引用、JNI 引用、活跃线程、同步锁对象。 追问:为什么不用「引用计数」——循环引用无法回收
  3. JDK 8 为什么用元空间替换永久代? 永久代大小要手工调、容易 PermGen space OOM,且与堆共用限额; 元空间用本地内存,默认不设上限,类卸载还能回收。 追问:字符串常量池在哪——JDK 7 起已在堆里
  4. G1 和 CMS 的核心差别? G1 可设停顿目标(MaxGCPauseMillis)、按 Region 收集、整体标记整理不会碎片化; CMS 用标记清除,会碎片,可能 concurrent mode failure 退化成 Serial Old 全停顿。 追问:G1 的回收流程——Young GC / 并发标记 / Mixed GC / Full GC
  5. 线上 Full GC 频繁怎么查? jstat -gcutil 看 FGC/FGCT 增速 → GC.class_histogram 找实例暴涨的类 → 必要时 dump 对比两次。 追问:两个 dump 怎么比——MAT 的 Histogram / Dominator Tree 对比
  6. TLAB 是干什么的? Thread Local Allocation Buffer,每个线程在 Eden 里分一小块独享,分配对象只用移动指针, 不用抢同一把锁。 追问:TLAB 满了怎么办——申请新 TLAB,或直接在 Eden 共享区 CAS 分配

二、Spring

  1. Bean 的生命周期顺序? 实例化 → 属性填充 → Aware → postProcessBeforeInitialization → @PostConstruct → afterPropertiesSet → init-method → postProcessAfterInitialization(此处生成 AOP 代理)。 追问:代理哪一步生成——最后一个 BPP 回调
  2. 三级缓存解决什么问题? 循环依赖时提前暴露一个「半成品引用」。第三级 singletonFactories 存的是工厂, 能在需要时生成 AOP 代理。 追问:第二级为什么存在——避免同一个 Bean 多次调用工厂生成不同代理
  3. 为什么构造器注入解决不了循环依赖? 构造器阶段就需要完整的依赖对象,无法先给半成品,直接抛 BeanCurrentlyInCreationException。 追问:Spring Boot 2.6 起默认怎么处理——禁止循环依赖
  4. @Transactional 什么情况下失效? 自调用、非 public、final 方法、对象不在容器里、异常被吞、跨线程(事务绑定在 ThreadLocal)。 追问:同类自调用怎么破——拆 Bean / AopContext / 注入自身代理
  5. BeanFactory 和 FactoryBean 的区别? BeanFactory 是容器本身;FactoryBean 是容器里的一个「造对象的工厂」, getBean("&xxx") 才拿到工厂本身。 追问:典型用途——MyBatis 的 MapperFactoryBean
  6. Spring Boot 自动装配靠什么生效? @Import(AutoConfigurationImportSelector) 读 META-INF/spring/…AutoConfiguration.imports(2.7 前是 spring.factories), 再按 @ConditionalOn* 过滤。 追问:3.0 升级后 starter 静默失效——旧文件路径不再被读

三、微服务

  1. Eureka 和 Nacos 的核心差异? Eureka 是 AP、纯客户端心跳、只做服务发现;Nacos 支持 AP/CP 切换(临时实例 AP,持久实例走 Raft CP), 还带配置中心。 追问:为什么注册中心多选 AP——分区时「能发现旧实例」好过「完全不可用」
  2. ephemeral=false 意味着什么? 实例变成持久实例,不靠客户端心跳,由服务端主动探活,数据走 CP 一致性,实例下线需显式注销。 追问:什么场景该用——IP 固定的中间件、非 Java 客户端
  3. 网关为什么基于 WebFlux? Netty 事件循环,少量线程扛大量连接。代价是一个阻塞调用就能卡住整条事件循环上的所有请求。 追问:必须调阻塞接口怎么办——切到 boundedElastic 调度器
  4. 熔断和降级的区别? 熔断是「发现下游不行了,短时间内不再调用」;降级是「调用失败时返回兜底结果」。 熔断是手段,降级是策略。 追问:三种状态——Closed / Open / Half-Open
  5. 配置中心动态刷新怎么实现的? @RefreshScope 的 Bean 被包在一个 Scope 代理里,刷新事件触发后销毁缓存实例, 下次调用重建。 追问:为什么静态字段刷不到——它不经过代理

四、并发

  1. HashMap 什么时候树化? 链表长度 ≥ 8 且 数组容量 ≥ 64;容量不足时优先扩容。退化阈值是 6。 追问:为什么两个阈值不一样——留出缓冲,避免反复树化/退化
  2. ConcurrentHashMap 怎么保证线程安全? JDK 8:桶为空用 CAS 插入,桶非空 synchronized 锁桶首节点;计数用 baseCount + CounterCell 分散竞争。 追问:为什么不用 JDK 7 的分段锁——粒度太粗、内存浪费
  3. 线程池核心参数与执行顺序? 核心线程 → 队列 → 最大线程 → 拒绝策略。Executors 的几个工厂方法要么队列无界 (FixedThreadPool),要么线程无界(CachedThreadPool),都可能 OOM。 追问:四种拒绝策略——Abort / CallerRuns / Discard / DiscardOldest
  4. ThreadLocal 为什么会内存泄漏? Entry 的 key 是 WeakReference<ThreadLocal>,value 是强引用。 ThreadLocal 被回收后 key 变 null,value 却挂在 Thread 的 Map 上;线程池复用线程时永不释放。 追问:正确用法——finally 里 remove()
  5. volatile 保证什么、不保证什么? 保证可见性与禁止指令重排(内存屏障),不保证原子性;i++ 依然会丢更新。 追问:DCL 单例为什么要加 volatile——防止「引用可见但对象未初始化完」
  6. synchronized 锁升级过程? 无锁 → 偏向锁 → 轻量级锁(CAS 自旋)→ 重量级锁(内核互斥量)。 追问:JDK 15 起偏向锁默认关闭,为什么——撤销成本高、现代场景收益低

五、集合与 Stream

  1. ArrayList 和 LinkedList 该怎么选? 绝大多数情况选 ArrayList。LinkedList 的「插入 O(1)」前提是你已经拿到了节点, 而按索引定位仍是 O(n),且节点分散导致缓存命中率差。 追问:ArrayList 扩容——1.5 倍,Arrays.copyOf
  2. 并行流有什么坑? 默认共用公共 ForkJoinPool(核数-1 个线程),一段慢任务会拖累全应用; 且 forEach 里写非线程安全集合会丢数据。 追问:什么时候值得用——纯 CPU 计算、数据量大、无共享状态
  3. 为什么说 Stream 不是「更快的 for」? 它买的是可读性与可组合性。简单遍历用 for 更快,Stream 有装箱与管道开销。 追问:mapToInt 为什么存在——避免装箱

六、IO 与 MyBatis

  1. BIO / NIO / AIO 的区别? BIO 一连接一线程;NIO 用 Selector 多路复用,一个线程管多连接; AIO 是内核完成读写后回调(Linux 上支持不如前两者成熟)。 追问:Netty 属于哪种——NIO(epoll)
  2. 零拷贝是什么? 省掉「内核缓冲区 ↔ 用户缓冲区」的来回拷贝。sendfile 让数据不出内核; mmap 把文件映射进用户空间。 追问:Kafka 用哪个——sendfile + 页缓存
  3. MyBatis 的 #{} 和 ${} 区别? #{} 走 PreparedStatement 占位符,防注入; ${} 是字符串直接拼接,只能用于表名/排序字段这类无法参数化的位置,且必须白名单校验。 追问:一级/二级缓存——SqlSession 级 / Mapper 级,二级默认关

七、怎么用这张表补短板

  • 答不上来的题,先点对应的地图节点(比读 4000 字长文快得多)。
  • 「追问」答不出来但第一问答得出来,说明你背过结论、没看过实现——面试官一追问就露底。
  • 每题都能说出一个你真实遇到过的现象,比背十个定义有用。