2026 Java后端高频面试宝典

文章目录

  • [一、Java 基础(必问 100%)](#一、Java 基础(必问 100%))
    • [1. 重载和重写的区别(必考)](#1. 重载和重写的区别(必考))
    • [2. == 和 equals 的区别](#2. == 和 equals 的区别)
    • [3. ArrayList 和 LinkedList 区别 & 业务选型](#3. ArrayList 和 LinkedList 区别 & 业务选型)
    • [4. 三种保证ArrayList线程安全的方案](#4. 三种保证ArrayList线程安全的方案)
    • [5. String / StringBuffer / StringBuilder 三者区别](#5. String / StringBuffer / StringBuilder 三者区别)
    • [6. 深拷贝与浅拷贝](#6. 深拷贝与浅拷贝)
    • [7. Cookie 和 Session 区别](#7. Cookie 和 Session 区别)
    • [8. HashMap 高频面试](#8. HashMap 高频面试)
      • [8.1 JDK7与JDK8底层数据结构](#8.1 JDK7与JDK8底层数据结构)
      • [8.2 初始化容量16、扩容2倍、负载因子0.75原因](#8.2 初始化容量16、扩容2倍、负载因子0.75原因)
      • [8.3 JDK7多线程扩容死循环,JDK8解决原理](#8.3 JDK7多线程扩容死循环,JDK8解决原理)
    • [9. ConcurrentHashMap 底层原理](#9. ConcurrentHashMap 底层原理)
  • [二、多线程 & 并发(面试重中之重)](#二、多线程 & 并发(面试重中之重))
    • [1. 线程六大生命周期状态](#1. 线程六大生命周期状态)
    • [2. 禁止使用Executors创建线程池原因](#2. 禁止使用Executors创建线程池原因)
    • [3. 线程池七大参数 + 四类拒绝策略](#3. 线程池七大参数 + 四类拒绝策略)
    • [4. 线程池核心线程数配置](#4. 线程池核心线程数配置)
    • [5. sleep()与wait()核心区别](#5. sleep()与wait()核心区别)
    • [6. volatile关键字作用](#6. volatile关键字作用)
    • [7. synchronized锁升级流程](#7. synchronized锁升级流程)
    • [8. synchronized和Lock区别](#8. synchronized和Lock区别)
    • [9. CAS乐观锁原理与缺点](#9. CAS乐观锁原理与缺点)
    • [10. ThreadLocal原理与内存泄漏](#10. ThreadLocal原理与内存泄漏)
    • [11. JUC三大工具类场景](#11. JUC三大工具类场景)
  • [三、JVM 核心面试题](#三、JVM 核心面试题)
    • [1. JVM运行时数据区](#1. JVM运行时数据区)
    • [2. 四种引用类型](#2. 四种引用类型)
    • [3. 双亲委派模型](#3. 双亲委派模型)
    • [4. 线上故障排查实战](#4. 线上故障排查实战)
  • 四、MySQL高频核心(索引+事务+锁)
    • [1. InnoDB与MyISAM区别](#1. InnoDB与MyISAM区别)
    • [2. ACID与四大隔离级别](#2. ACID与四大隔离级别)
    • [3. 索引三大核心:组合索引、回表、覆盖索引](#3. 索引三大核心:组合索引、回表、覆盖索引)
      • [3.1 组合索引(联合索引)](#3.1 组合索引(联合索引))
      • [3.2 回表](#3.2 回表)
      • [3.3 覆盖索引](#3.3 覆盖索引)
    • [4. 索引失效常见场景](#4. 索引失效常见场景)
    • [5. B+树适配MySQL原因](#5. B+树适配MySQL原因)
  • [五、Spring / SpringBoot 高频](#五、Spring / SpringBoot 高频)
    • [1. IOC与DI](#1. IOC与DI)
    • [2. AOP动态代理](#2. AOP动态代理)
    • [3. 三级缓存解决单例setter循环依赖](#3. 三级缓存解决单例setter循环依赖)
    • [4. @Transactional事务失效高频场景](#4. @Transactional事务失效高频场景)
    • [5. Bean完整生命周期](#5. Bean完整生命周期)
  • 六、Redis高频面试(必考100%)
    • [1. 五大基础数据类型+落地场景](#1. 五大基础数据类型+落地场景)
    • [2. Redis高性能四点原因](#2. Redis高性能四点原因)
    • [3. 缓存三大经典问题](#3. 缓存三大经典问题)
      • [3.1 缓存穿透(查询数据库不存在的数据,频繁打DB)](#3.1 缓存穿透(查询数据库不存在的数据,频繁打DB))
      • [3.2 缓存击穿(热点key瞬间过期,大量请求击穿到DB)](#3.2 缓存击穿(热点key瞬间过期,大量请求击穿到DB))
      • [3.3 缓存雪崩(大批量key同一时间过期/Redis宕机,全量请求落库压垮DB)](#3.3 缓存雪崩(大批量key同一时间过期/Redis宕机,全量请求落库压垮DB))
    • [4. Redisson分布式锁原理](#4. Redisson分布式锁原理)
    • [5. Redis持久化](#5. Redis持久化)
  • [七、分布式 & MQ高频](#七、分布式 & MQ高频)
    • [1. CAP & BASE理论](#1. CAP & BASE理论)
    • [2. 分布式事务五种方案](#2. 分布式事务五种方案)
    • [3. MQ核心面试](#3. MQ核心面试)
  • 八、大厂高频实战场景题(面试加分)
  • 九、面试精简背诵总结

一、Java 基础(必问 100%)

1. 重载和重写的区别(必考)

重载(Overload)

  • 作用范围:在同一个类内部
  • 规则:方法名相同,参数列表(个数/类型/顺序)不同,返回值类型不作约束
  • 绑定时机:编译期静态绑定,属于静态多态
  • 业务场景:工具类提供多参数查询方法,例如根据ID查用户、ID+姓名查用户

重写(Override)

  • 作用范围:父子类继承关系中
  • 规则:方法名、参数列表、返回值(协变)完全一致,不能缩小父类方法访问权限;private/static/final修饰的父方法无法重写
  • 绑定时机:运行期动态绑定,属于动态多态
  • 业务场景:普通订单、会员订单各自重写支付pay()方法,实现不同扣款逻辑

2. == 和 equals 的区别

  1. 基本数据类型:== 直接对比存储的数值
  2. 引用数据类型:== 对比堆内存对象地址
  3. Object原生equals()等价于==;String、Integer等包装类重写equals,改为对比对象存储内容

面试场景题:new String("a") == new String("a") → false;new String("a").equals(new String("a")) → true

3. ArrayList 和 LinkedList 区别 & 业务选型

  • ArrayList:底层动态数组,随机下标查询速度快,中间位置增删元素需要移位效率低,默认初始容量10,扩容1.5倍
  • LinkedList:底层双向链表,首尾节点增删O(1),随机下标查找需要遍历链表效率低,无扩容机制

选型口诀:多查少改用ArrayList(商品列表查询),频繁首尾插入删除用LinkedList(临时浏览记录队列)

4. 三种保证ArrayList线程安全的方案

  1. Vector:方法全部加synchronized,老旧API,全局锁并发性能差,项目不推荐
  2. Collections.synchronizedList:对原有List做包装,全方法加同步锁,并发效率一般
  3. CopyOnWriteArrayList:生产首选,写时复制新数组、读操作无锁,适合读多写少的业务场景(配置缓存列表)

5. String / StringBuffer / StringBuilder 三者区别

  • String:底层final字符数组,字符串拼接会频繁创建新对象,少量字符串拼接场景使用
  • StringBuffer:方法加synchronized同步锁,多线程安全,并发拼接性能偏低,多线程字符串拼接
  • StringBuilder:无同步锁,执行效率最高,单线程大批量拼接首选(日志内容组装)

6. 深拷贝与浅拷贝

  • 浅拷贝:仅拷贝对象引用地址,新旧对象共用子对象数据,修改其中一方会互相影响
  • 深拷贝:递归完整创建所有层级新对象,内存完全隔离,修改互不干扰

场景:接口返回订单VO对象,防止修改VO污染原数据库实体,使用深拷贝。

分类 Cookie Session
存储位置 客户端浏览器 服务端内存
容量 单条最大3KB,仅支持字符串 无固定限制,可存任意对象
安全性 明文存储可被篡改,不安全 服务端保存,安全性高

业务规范:登录账号密码等敏感信息存Session;记住登录、个性化偏好配置存入Cookie;浏览器禁用Cookie时可通过URL拼接sessionId。

8. HashMap 高频面试

8.1 JDK7与JDK8底层数据结构

  • JDK1.7:数组+单向链表
  • JDK1.8:数组+链表+红黑树
  • 树化规则:链表长度≥8 并且 数组容量≥64时链表转为红黑树;链表长度≤6红黑树退回链表

8.2 初始化容量16、扩容2倍、负载因子0.75原因

  1. 容量2的幂次:(数组长度-1)&hash等价取模运算,效率远高于%;扩容后元素只落在原下标/原下标+旧容量,减少数据迁移
  2. 负载因子0.75:平衡存储空间占用与哈希冲突概率,过小浪费内存、过大链表过长查询变慢

8.3 JDK7多线程扩容死循环,JDK8解决原理

JDK7采用头插法,多线程扩容时链表倒置形成环形链表,get()无限循环死锁;JDK8改为尾插法,保留原有链表顺序,彻底规避死循环。

9. ConcurrentHashMap 底层原理

  • JDK7:分段锁Segment,16个分段各自加ReentrantLock,锁粒度大、内存开销高
  • JDK8:取消分段,采用CAS + synchronized锁住数组桶头节点,锁粒度更细、并发性能更强

不用Lock原因:JDK1.6后synchronized经过偏向锁/轻量级锁优化,性能对标Lock,占用内存更小。

二、多线程 & 并发(面试重中之重)

1. 线程六大生命周期状态

NEW(新建)→RUNNABLE(就绪/运行)→BLOCKED(阻塞抢锁失败)→WAITING(无限等待wait)→TIMED_WAITING(限时sleep/wait(time))→TERMINATED(线程终止)

2. 禁止使用Executors创建线程池原因

Executors工具类创建的线程池存在OOM风险:

  1. newCachedThreadPool:无上限创建线程,高并发瞬间线程爆炸
  2. newFixedThreadPool/newSingleThreadExecutor:无界阻塞队列,任务无限堆积占满堆内存

生产规范:手动通过ThreadPoolExecutor自定义线程池参数

3. 线程池七大参数 + 四类拒绝策略

七大参数:核心线程数、最大线程数、空闲存活时间、时间单位、阻塞队列、线程工厂、拒绝策略

  1. AbortPolicy(默认):直接抛出任务满异常
  2. CallerRunsPolicy:让提交任务的主线程执行任务(生产最稳妥)
  3. DiscardPolicy:默默丢弃新任务不报错
  4. DiscardOldestPolicy:丢弃队列头部最早未执行任务

4. 线程池核心线程数配置

  • CPU密集型(大量计算):CPU物理核心数,减少上下文切换
  • IO密集型(DB/第三方接口调用):CPU核心数×2,等待IO时复用线程

5. sleep()与wait()核心区别

  1. sleep():Thread静态方法、不释放同步锁、时间到自动唤醒、任意代码块都能调用
  2. wait():Object成员方法、释放同步锁、必须写在synchronized同步块内、需要notify/notifyAll手动唤醒

6. volatile关键字作用

保证变量可见性、禁止指令重排序,无法保证原子性

  • 内存屏障强制修改立即刷入主内存,其他线程实时读取最新值
  • i++分为读取、修改、写入三步非原子操作,volatile无法解决并发i++安全问题

7. synchronized锁升级流程

无锁 → 偏向锁(单线程无竞争)→ 轻量级自旋锁(少量竞争)→ 重量级Monitor锁(大量线程争抢)

8. synchronized和Lock区别

  • synchronized:JVM底层实现,代码执行完毕/异常自动释放锁、不可中断、只支持非公平锁、无法精准唤醒指定线程
  • Lock:API手动实现,必须finally手动unlock释放锁、支持可中断、可选公平/非公平锁、多Condition精准唤醒指定等待线程

9. CAS乐观锁原理与缺点

原理:比较内存当前值与预期值,一致则更新数据,不一致自旋重试

缺点:ABA数据篡改问题、无限自旋空耗CPU、仅支持单个变量原子更新;ABA解决方案:添加版本号/时间戳

10. ThreadLocal原理与内存泄漏

  • 每个线程自带独立ThreadLocalMap,存储线程私有数据
  • Map初始容量16,扩容阈值2/3,哈希冲突采用线性探测向后找空位(HashMap是拉链法)
  • 泄漏原因:key是弱引用可被GC回收,value是强引用常驻内存;用完必须手动remove()释放

11. JUC三大工具类场景

  1. CountDownLatch:等待全部子任务执行完毕,主线程再继续执行(多分片报表汇总)
  2. CyclicBarrier:凑齐设定数量线程统一同时放行,支持循环复用
  3. Semaphore:信号量控制同一时间并发线程数(接口限流)

三、JVM 核心面试题

1. JVM运行时数据区

线程私有:程序计数器(唯一不会OOM区域)、虚拟机栈、本地方法栈

线程共享:堆(对象实例存放,GC主要区域)、方法区(JDK8改为元空间,使用本地物理内存)

JDK8废除永久代PermGen,彻底解决永久代OOM问题

2. 四种引用类型

  • 强引用:new创建对象,GC永远不会回收
  • 软引用:堆内存空间不足时才触发回收(缓存实现)
  • 弱引用:每次GC触发必回收(ThreadLocal的key)
  • 虚引用:仅用来追踪对象回收状态

3. 双亲委派模型

加载类时子类加载器优先向上委托父加载器加载,顶层启动类加载器无法加载再由子类加载;作用:防止JDK核心类被自定义类篡改、避免类重复加载。

4. 线上故障排查实战

  1. CPU占满100%:top查进程 → top -Hp定位耗CPU线程 → 转16进制 → jstack排查死循环代码
  2. 频繁FullGC:jmap导出堆dump文件,MAT工具分析大对象/内存泄漏

四、MySQL高频核心(索引+事务+锁)

1. InnoDB与MyISAM区别

  • InnoDB:支持事务、行级锁、MVCC、崩溃故障恢复、聚簇索引,生产业务表首选
  • MyISAM:表级锁、不支持事务、查询速度快,静态文章、只读归档表使用

2. ACID与四大隔离级别

ACID:原子性(undo日志回滚)、一致性、隔离性(MVCC实现)、持久性(redo刷盘落地)

隔离级别:读未提交→读已提交→**可重复读(InnoDB默认,间隙锁防幻读)**→串行化

3. 索引三大核心:组合索引、回表、覆盖索引

3.1 组合索引(联合索引)

多个字段联合创建索引,严格遵循最左前缀原则 ,查询条件缺失最左侧首字段,索引失效。

例:索引idx(phone,id,name),where id=?无法走索引。

3.2 回表

二级索引只存储主键值,根据主键再去聚簇索引查询完整行数据,额外IO开销即回表。

3.3 覆盖索引

查询需要的所有字段全部包含在联合索引中,无需回表查询原表,性能最优。

场景:select id,name from user where phone='138xxxx',索引(phone,id,name),命中覆盖索引。

4. 索引失效常见场景

  1. like左模糊%xxx
  2. 索引字段做运算、调用函数
  3. 字符串数字隐式类型转换
  4. or条件一侧字段无索引
  5. 违背最左前缀原则
  6. !=、is not null大概率不走索引

5. B+树适配MySQL原因

  • 非叶子节点只存索引键,树层级少、磁盘IO次数少
  • 叶子节点双向链表串联,范围查询、排序性能极强

五、Spring / SpringBoot 高频

1. IOC与DI

IOC控制反转:对象创建管理权从业务代码转交Spring容器,不再手动new;

DI依赖注入:容器自动注入Bean依赖,实现代码解耦。

2. AOP动态代理

  • JDK动态代理:目标类实现接口时使用
  • CGLIB代理:目标无接口,继承子类实现代理
    落地场景:接口日志记录、权限校验、全局异常、事务管理

3. 三级缓存解决单例setter循环依赖

  1. 一级缓存singletonObjects:完整初始化完毕的成品Bean
  2. 二级缓存earlySingletonObjects:半成品提前暴露Bean
  3. 三级缓存singletonFactories:ObjectFactory工厂,解决AOP代理提前暴露

仅支持单例+setter注入;构造注入、多例Bean无法解决循环依赖

4. @Transactional事务失效高频场景

  1. 方法非public修饰
  2. 同类内部方法互相调用(A方法无注解调用带@Transactional的B)
  3. 业务异常被try-catch捕获吃掉,无法触发回滚
  4. 事务传播属性配置错误
  5. 默认只回滚RuntimeException,非运行异常不回滚

5. Bean完整生命周期

实例化 → 属性填充赋值 → 各类Aware接口回调 → BeanPostProcessor后置处理 → @PostConstruct → init-method → 容器使用 → @PreDestroy → destroy销毁

六、Redis高频面试(必考100%)

1. 五大基础数据类型+落地场景

  • String:短信验证码、接口PV计数器、商品库存
  • Hash:用户基础信息、购物车数据
  • List:简易消息队列、用户浏览历史
  • Set:用户好友、抽奖去重、共同好友交集
  • ZSet:排行榜、延时任务队列

2. Redis高性能四点原因

  1. 全部基于内存操作
  2. 命令执行单线程无锁竞争(6.0仅IO多线程)
  3. IO多路复用模型处理客户端连接
  4. 底层高效简约数据结构

3. 缓存三大经典问题

3.1 缓存穿透(查询数据库不存在的数据,频繁打DB)

方案:布隆过滤器拦截非法key、空值缓存、接口参数合法性校验

3.2 缓存击穿(热点key瞬间过期,大量请求击穿到DB)

方案:热点key永不过期、互斥锁、分布式锁

3.3 缓存雪崩(大批量key同一时间过期/Redis宕机,全量请求落库压垮DB)

方案:过期时间随机±30分钟打散、Redis集群高可用、接口限流熔断

4. Redisson分布式锁原理

可重入锁、看门狗自动续期防死锁、Lua脚本原子加解锁;集群环境采用红锁方案规避主从切换丢锁。

5. Redis持久化

  • RDB:定时全量快照,恢复速度快,可能丢失最后一段时间数据
  • AOF:逐条记录写指令,数据安全,文件体积大
  • 生产推荐:RDB+AOF混合持久化

七、分布式 & MQ高频

1. CAP & BASE理论

CAP:分布式系统分区容错P必存在,只能在一致性C/可用性A中二选一(CP/AP架构);

BASE:基本可用、软状态、最终一致性,互联网分布式主流设计思想。

2. 分布式事务五种方案

  1. 强一致:2PC、TCC、Seata AT(项目最常用无侵入方案)
  2. 最终一致:本地消息表、RocketMQ事务消息

3. MQ核心面试

作用:系统解耦、异步处理、流量削峰

  • 防消息丢失:生产者confirm确认、消息队列持久化、消费者手动ACK
  • 防重复消费:业务幂等设计(唯一索引/Redis去重)
  • 消息积压:扩容消费者、批量消费
  • 死信队列:消息超时、业务异常拒收、队列塞满转入死信

八、大厂高频实战场景题(面试加分)

  1. 秒杀商品超卖如何解决?
    Redis预扣库存 + Redisson分布式锁 + 接口限流 + 数据库最终落地扣减
  2. 订单超时自动关闭
    Redis ZSet延时队列 / RocketMQ延迟消息 / 定时任务轮询
  3. 高并发接口优化思路
    多级缓存、异步化MQ、限流熔断、读写分离、分库分表
  4. SQL慢查询优化
    explain执行计划分析、合理建联合+覆盖索引、规避索引失效、优化分页
  5. 接口幂等性方案
    全局唯一令牌、数据库唯一约束、业务状态机、Redis去重
  6. 构造器注入产生的循环依赖能解决吗?
    答:不能。构造器注入是在实例化阶段(new A(B))就产生依赖,而 Spring 的三级缓存是在实例化后、属性注入阶段解决循环依赖,因此构造器循环依赖会直接抛出 BeanCurrentlyInCreationException。
  7. 多例(Prototype)Bean 通过 setter 注入产生的循环依赖能解决吗?
    答:不能。多例 Bean 每次getBean()都会新建对象,Spring 不会对多例 Bean 使用三级缓存缓存,因此循环依赖时会无限递归创建对象,最终抛出异常。
  8. 若只有一级缓存(map1),能解决循环依赖吗?
    答:从解决循环依赖的角度能执行,但使用过程会有问题。若成品和半成品都存在 map1 中,当 A 依赖 B、B 又依赖 A 时,A 在半成品阶段被 B 引用,后续 A 初始化完成后属性可能不完整,导致空指针或逻辑错误。
  9. 若只有一级、二级缓存(map1+map2),能解决循环依赖吗?
    答:在无 AOP 代理的情况下可以解决;有 AOP 时无法解决。
    无 AOP:实例化 A→放入 map2(半成品)→A 依赖 B→实例化 B→B 依赖 A 时从 map2 取 A 的半成品→B 初始化完成放入 map1→A 注入 B 后完成初始化→A 放入 map1,流程通顺。
    有 AOP:A 的代理对象需要在初始化后生成,若只有 map1(成品)和 map2(半成品),B 在属性注入时拿到的是 A 的半成品(无代理),后续 A 生成代理后,B 引用的还是旧对象,导致代理不一致。
  10. 三级缓存的核心作用是什么?
    答:解决 "循环依赖" 与 "AOP 代理" 的兼容性问题。
    无循环依赖时:保证 Bean 的代理对象在初始化最后阶段生成,遵循 Spring "初始化完成后生成代理" 的设计原则。
    有循环依赖时:通过 ObjectFactory 提前暴露 Bean 的早期引用(若有 AOP 则直接生成代理对象),确保循环依赖的 Bean 能拿到正确的代理引用。
  11. 循环依赖中,AOP 代理对象何时生成?
    答:在 BeanPostProcessor#after 阶段生成。若存在循环依赖,会提前通过三级缓存的 ObjectFactory 生成代理对象;若不存在循环依赖,则在初始化完成后生成,且 AOP 源码会判断是否已生成过代理,避免重复创建。
  12. 只有一级缓存和三级缓存能解决循环依赖吗?
    当 Bean A 依赖 Bean B,Bean B 又依赖 Bean A 时,B 在获取 A 的早期引用时,只能从三级缓存的工厂反复生成新的引用(而非复用同一引用),会导致循环引用的 Bean 不是同一实例,最终引发逻辑错误或空指针异常。
  13. spring的bean是线程安全的吗?
    实际上⼤部分时候 spring bean ⽆状态的(⽐如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean有状态的话(⽐如 view model 对象),那就要开发者⾃⼰去保证线程安全了,最简单的就是改变 bean 的作⽤域,把"singleton"变更为"prototype",这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。
    如果要保证线程安全
    1.可以将bean的作用域改为prototype,比如像Model View。
    2.采用ThreadLocal来解决线程安全问题。ThreadLocal为每个线程保存一个副本变量,每个线程只操作自己的副本变量。

九、面试精简背诵总结

  • 想要提速:多线程
  • 并发防脏数据:本地锁/分布式锁
  • 查询SQL慢大概率:回表、没使用覆盖索引
  • 缓存故障:穿透、击穿、雪崩
  • 事务失效高频:同类内部调用、非public、异常被catch
  • 高并发架构核心:缓存+异步MQ+限流+集群
相关推荐
微小冷1 小时前
Julia卫星工具箱SatelliteToolbox简介
开发语言·航天·坐标转换·julia·卫星工具箱
2601_colin2 小时前
Codex插件全流程实战指南
开发语言·经验分享·笔记·微信开放平台
Song_da_da_2 小时前
C#与VisionPro联合编程实战:机器视觉二次开发完整指南
开发语言·microsoft·c#
xyzzklk2 小时前
解决Salesforce无法向外发送邮件
android·java·开发语言·网络·crm·salesforce·客户关系管理
biubiubiu07062 小时前
SpringBoot关于外部化配置
java·spring boot·spring
cuso4win2 小时前
Feed 流面试笔记
笔记·面试·职场和发展
zzz_23682 小时前
【Spring】面试突击系列(二):SpringBoot 入门与自动配置原理
java·spring boot·spring
Full Stack Developme3 小时前
Spring AOP 与 AspectJ
java·后端·spring
小蒋聊技术3 小时前
电商系列第九课:结算中心 —— 电商财务底盘,资金分账与 AI 智能化演进
人工智能·面试·职场和发展