Java 后端面试的问题主要围绕基础能力、核心技术、框架应用、项目经验、性能优化、工程化 六大维度展开,不同公司 / 职级(校招 / 社招、初级 / 中高级)的侧重点不同,下面按优先级和高频度整理,方便你针对性准备:
一、Java 基础(必问,所有级别)
这是面试的 "敲门砖",校招 / 初级岗会深挖,中高级岗侧重结合场景提问。
-
核心语法 & 特性
- 面向对象三大特性(封装 / 继承 / 多态)的理解 + 实际应用场景(比如多态在 Spring 中的体现)
- 接口 vs 抽象类(区别、使用场景)
- 重载(Overload)vs 重写(Override)(区别、底层原理)
- 异常体系:Checked Exception vs Unchecked Exception,常见异常(NullPointerException/IndexOutOfBoundsException 等)的产生原因和解决方案,try-catch-finally 的执行顺序(比如 finally 中修改返回值的坑)
- 泛型:作用、通配符(? extends T / ? super T)、泛型擦除及影响
- 注解:自定义注解的实现、元注解(@Target/@Retention 等),Spring 中常用注解的原理(比如 @Autowired)
-
JVM(重中之重)
- 运行时数据区(方法区 / 堆 / 栈 / 程序计数器 / 本地方法栈),各区域的作用、OOM 可能发生的区域
- 垃圾回收(GC):常见垃圾回收器(Serial/Parallel/CMS/G1/ZGC),GC 算法(标记 - 清除 / 标记 - 整理 / 复制),判断对象存活的方式(引用计数法 / 可达性分析),强 / 软 / 弱 / 虚引用的区别及使用场景(比如弱引用在 ThreadLocal 中的应用)
- 类加载机制:双亲委派模型(原理、好处、如何打破),类加载的过程(加载 / 验证 / 准备 / 解析 / 初始化)
- 内存泄漏 vs 内存溢出(区别、常见场景:比如静态集合持有对象、未关闭的连接)
-
集合框架
- 常用集合:ArrayList vs LinkedList(底层实现、查询 / 增删效率、使用场景),HashMap(JDK7 vs JDK8 底层变化:数组 + 链表→数组 + 链表 / 红黑树,哈希冲突解决方式,扩容机制,线程不安全的原因)
- 线程安全集合:ConcurrentHashMap(JDK7 vs JDK8 实现差异,锁粒度:分段锁→CAS+Synchronized),Vector/Hashtable(为什么不推荐用)
- HashSet 的底层实现(基于 HashMap),TreeMap 的排序原理
-
多线程 & 并发
- 线程创建方式(继承 Thread / 实现 Runnable/Callable+Future),线程的生命周期(新建 / 就绪 / 运行 / 阻塞 / 终止)
- 线程安全问题:可见性 / 原子性 / 有序性,volatile 关键字(作用、底层原理:内存屏障)
- 锁机制:Synchronized(底层:偏向锁 / 轻量级锁 / 重量级锁,锁升级过程)、Lock 接口(ReentrantLock、公平锁 / 非公平锁),Synchronized vs Lock 的区别
- 并发工具类:CountDownLatch/CyclicBarrier/Semaphore(作用、使用场景、区别),ThreadPoolExecutor(核心参数、拒绝策略、常见线程池(FixedThreadPool/CachedThreadPool)的问题)
- 线程池的核心参数:corePoolSize/maximumPoolSize/keepAliveTime/workQueue,如何合理设置线程池参数
二、Java 核心技术(中高级岗重点)
-
IO/NIO
- BIO/NIO/AIO 的区别、使用场景,NIO 的核心组件(Channel/Buffer/Selector),Reactor 模式
- 零拷贝(mmap/sendfile)的原理和应用(比如 Netty、kafka)
-
网络编程
- TCP/IP 三次握手 / 四次挥手(为什么三次握手、四次挥手),TCP 的可靠性机制(重传 / 滑动窗口 / 拥塞控制)
- HTTP 协议:HTTP1.0/1.1/2.0 的区别,GET vs POST(区别、幂等性),HTTPS 的加密过程(非对称加密 + 对称加密 + CA 证书)
三、框架 & 中间件(后端核心,所有级别)
-
Spring 全家桶(必问)
- Spring Core:IOC 容器(原理、Bean 的生命周期、Bean 的作用域(singleton/prototype)、循环依赖的解决方式),AOP(原理:动态代理(JDK/CGLIB)、切面 / 切点 / 通知、应用场景:事务 / 日志 / 权限)
- Spring Boot:自动配置原理(@EnableAutoConfiguration、SPI 机制),starter 的实现原理,配置优先级
- Spring MVC:工作流程(DispatcherServlet 的作用、九大组件),参数绑定的原理
- Spring Transaction:事务的传播特性、隔离级别,事务失效的场景(比如非 public 方法、异常捕获未抛出、多线程)
-
MyBatis
- 核心原理:SqlSessionFactory/SqlSession/Mapper 接口的动态代理,一级缓存 / 二级缓存
- 动态 SQL 的实现,#{} vs ${}(区别、SQL 注入问题)
- 分页实现(RowBounds vs 分页插件)
-
中间件(高频)
- MySQL :
- 索引:聚簇索引 / 非聚簇索引,B + 树索引的原理,索引失效的场景(like % xxx、字段类型不匹配等)
- 事务:ACID 特性,隔离级别(读未提交 / 读已提交 / 可重复读 / 串行化),MVCC 的原理(版本链 /undo log/ReadView)
- 锁:行锁 / 表锁,乐观锁 / 悲观锁,死锁的产生和解决
- 优化:慢查询优化(explain 执行计划),分库分表(Sharding-JDBC),主从复制原理
- Redis :
- 数据结构:String/List/Hash/Set/ZSet 的底层实现(比如 String 的 SDS、Hash 的 ziplist/hashtable),使用场景
- 过期策略:惰性删除 / 定期删除,内存淘汰策略(LRU/LFU 等)
- 持久化:RDB vs AOF(区别、优缺点),主从复制原理,哨兵模式 / 集群模式
- 缓存问题:缓存穿透 / 缓存击穿 / 缓存雪崩(原因、解决方案)
- 消息队列(RabbitMQ/Kafka) :
- 核心概念:交换机 / 队列(RabbitMQ),主题 / 分区 / 副本(Kafka)
- 消息可靠性:生产者确认、消费者确认、死信队列,消息重复消费的解决(幂等性)
- Kafka:高可用原理(ISR 机制),分区分配策略,消费者 Rebalance
- 其他:Nginx(反向代理 / 负载均衡)、Elasticsearch(倒排索引、分词、集群架构)、分布式锁(Redis/Zookeeper 实现)
- MySQL :
四、项目经验 & 场景题(社招 / 中高级岗核心)
-
项目深挖
- 项目的核心业务、技术架构(画架构图),你负责的模块和核心功能
- 项目中的难点问题(比如性能瓶颈、高并发、数据一致性),如何分析、解决,最终效果
- 技术选型的原因(比如为什么用 Redis 不用 Memcached,为什么用 Kafka 不用 RabbitMQ)
-
场景题
- 高并发场景:秒杀系统设计(限流、削峰、防超卖),接口防刷(令牌桶 / 漏桶)
- 数据一致性:分布式事务(2PC/TCC/SAGA/ 最终一致性),缓存与数据库的一致性
- 高可用:服务降级 / 熔断(Sentinel/Hystrix),服务注册发现(Nacos/Eureka)
五、工程化 & 软实力
- 工程化:Git 版本管理(分支策略),Maven/Gradle(依赖管理、打包),Docker 容器化,CI/CD 流程
- 设计模式:单例模式(饿汉 / 懒汉 / 双重检查锁的坑)、工厂模式、代理模式、观察者模式等(理解 + 应用场景)
- 软实力:技术学习方法、项目中遇到的最大挑战、职业规划
总结
- 基础为王:Java 基础(JVM、多线程、集合)是所有面试的核心,必须吃透原理 + 场景;
- 框架 / 中间件:Spring 核心(IOC/AOP)、MySQL(索引 / 事务)、Redis(数据结构 / 缓存问题)是高频考点,要结合项目讲清应用;
- 项目导向:社招 / 中高级岗更看重 "解决问题的能力",准备 1-2 个核心项目,深挖难点和解决方案,而非单纯罗列功能。
不同职级的准备重点:校招 / 初级岗侧重基础 + 框架使用;中高级岗侧重原理 + 性能优化 + 架构设计;资深岗侧重技术选型 + 复杂场景解决方案。
有哪些常见的Java后端面试题及答案?
一、Java 基础(必背)
1. 面向对象三大特性及应用场景?
答案:
- 封装:隐藏对象的属性和实现细节,仅对外暴露接口。比如 JavaBean 的私有属性 + get/set 方法,Spring Bean 封装了核心逻辑,对外提供调用接口。
- 继承 :子类继承父类的属性和方法,提高代码复用。比如 Spring 的
AbstractApplicationContext作为父类,子类(AnnotationConfigApplicationContext)复用核心逻辑。 - 多态 :同一行为不同表现形式(编译时类型和运行时类型不一致)。比如 Spring 中
ApplicationContext接口的不同实现类(ClassPathXmlApplicationContext、AnnotationConfigApplicationContext),调用getBean()时表现不同,是多态的典型应用。
2. 接口和抽象类的区别?
| 维度 | 接口(Interface) | 抽象类(Abstract Class) |
|---|---|---|
| 成员变量 | 只能是public static final |
可以有任意修饰符的成员变量 |
| 方法 | JDK8 前:只能抽象方法;JDK8 后:可定义默认方法 / 静态方法 | 可以有抽象方法,也可以有普通方法 |
| 继承 / 实现 | 类可实现多个接口 | 类只能继承一个抽象类 |
| 构造方法 | 无 | 有 |
| 使用场景 | 定义规范 / 能力(比如Runnable) |
复用代码 + 定义规范(比如AbstractList) |
3. HashMap JDK7 和 JDK8 的区别?
答案:
| 维度 | JDK7 | JDK8 |
|---|---|---|
| 底层结构 | 数组 + 链表 | 数组 + 链表 / 红黑树(链表长度≥8 且数组长度≥64 时转红黑树) |
| 哈希计算 | 4 次位运算 + 1 次取模 | 扰动函数(多次异或 + 右移)+ 取模 |
| 插入方式 | 头插法(扩容时可能导致链表循环) | 尾插法(解决循环问题) |
| 扩容机制 | 扩容后重新计算所有元素的哈希 | 扩容后通过(hash & oldCap) == 0判断元素是否需要移动,减少计算 |
| 线程安全 | 不安全(并发扩容导致死循环) | 不安全(但死循环问题解决) |
4. volatile 关键字的作用?
答案:
- 可见性:一个线程修改 volatile 变量后,其他线程能立即看到最新值(底层通过内存屏障,禁止 CPU 缓存,直接读写主存)。
- 有序性 :禁止指令重排序(比如 DCL 单例中,volatile 防止
instance = new Singleton()被拆分为 "分配内存→初始化→赋值" 的重排序,导致其他线程拿到未初始化的对象)。 - 不保证原子性 :比如
i++(读取 - 修改 - 写入)仍会线程不安全,需配合AtomicInteger或锁。
5. 线程池的核心参数及如何合理设置?
答案:核心参数(ThreadPoolExecutor):
corePoolSize:核心线程数(常驻线程,即使空闲也不销毁);maximumPoolSize:最大线程数(核心线程 + 临时线程的总数);keepAliveTime:临时线程空闲超时时间;workQueue:任务队列(核心线程满后,任务先入队,队列满才创建临时线程);handler:拒绝策略(队列满 + 线程数达最大值时触发,比如 AbortPolicy(抛异常)、CallerRunsPolicy(调用者执行))。
合理设置:
- CPU 密集型任务(比如计算):核心线程数 = CPU 核心数 + 1(减少线程切换);
- IO 密集型任务(比如数据库 / 网络调用):核心线程数 = CPU 核心数 × 2(IO 等待时线程空闲,可利用多核);
- 队列选择:用有界队列(比如 ArrayBlockingQueue),避免无界队列(LinkedBlockingQueue)导致内存溢出;
- 拒绝策略:非核心任务用 CallerRunsPolicy(降级),核心任务用 AbortPolicy(快速失败)。
6. JVM 运行时数据区有哪些?OOM 可能发生在哪些区域?
答案:运行时数据区(JDK8):
- 程序计数器:记录线程执行的字节码行号,唯一不会 OOM 的区域;
- 虚拟机栈 :存储方法栈帧(局部变量、操作数栈等),栈深度超限抛
StackOverflowError,创建线程过多 / 栈帧过大抛 OOM; - 本地方法栈:为 Native 方法服务,和虚拟机栈类似,会抛 StackOverflowError/OOM;
- 堆 :存储对象实例(new 的对象),最易 OOM(
java.lang.OutOfMemoryError: Java heap space); - 方法区(元空间) :存储类信息、常量、静态变量等,JDK8 前是永久代,OOM 为
PermGen space;JDK8 后是元空间(占用本地内存),但元空间大小不足也会 OOM。
二、Spring 全家桶(高频)
7. Spring IOC 容器的原理?
答案:IOC(控制反转):将对象的创建、依赖注入由程序员控制转为 Spring 容器控制,核心流程:
- 加载配置:读取 XML / 注解配置,解析出需要创建的 Bean 定义(BeanDefinition);
- 实例化 Bean:通过反射创建 Bean 实例(单例 Bean 默认在容器启动时创建,原型 Bean 每次获取时创建);
- 依赖注入:将 Bean 的依赖对象注入(比如 @Autowired);
- 初始化 Bean :执行
@PostConstruct、InitializingBean的afterPropertiesSet()方法; - Bean 就绪:存入单例池(singletonObjects),供后续获取;
- 销毁 Bean :容器关闭时,执行
@PreDestroy、DisposableBean的destroy()方法。
8. Spring 如何解决循环依赖?
答案 :循环依赖:A 依赖 B,B 依赖 A(仅支持单例 Bean,原型 Bean 无法解决),核心靠 "三级缓存":
- 一级缓存(singletonObjects):存储完全初始化的单例 Bean;
- 二级缓存(earlySingletonObjects):存储提前暴露的、未完成初始化的 Bean;
- 三级缓存(singletonFactories):存储 Bean 的工厂方法,用于创建代理对象。
核心流程:
- A 创建时,先存入三级缓存→去创建 B;
- B 创建时依赖 A,从三级缓存获取 A 的半成品→存入二级缓存,B 完成初始化;
- A 从二级缓存拿到 B,完成初始化,存入一级缓存,删除二 / 三级缓存。
9. Spring AOP 的原理及应用场景?
答案 :AOP(面向切面编程):将日志、事务、权限等通用逻辑(切面)与业务逻辑分离,核心原理是动态代理:
- JDK 动态代理:目标类实现接口时使用,生成实现相同接口的代理类,通过反射调用目标方法;
- CGLIB 动态代理:目标类无实现接口时使用,继承目标类生成子类,重写目标方法(需注意:目标方法不能是 final)。
核心概念:
- 切面(Aspect):通用逻辑(比如事务切面);
- 切点(Pointcut):匹配要增强的方法(比如
execution(* com.service.*.*(..))); - 通知(Advice):增强的时机(前置 @Before、后置 @After、返回 @AfterReturning、异常 @AfterThrowing、环绕 @Around)。
应用场景:事务管理、日志记录、权限校验、性能监控。
10. Spring 事务失效的常见场景?
答案:
- 方法不是 public:Spring 事务基于 AOP,JDK 动态代理只拦截 public 方法;
- 异常被捕获且未抛出 :比如
try{...} catch(Exception e) {},事务无法感知异常,不会回滚; - 抛出非运行时异常 :默认事务只回滚
RuntimeException和Error,需手动指定@Transactional(rollbackFor = Exception.class); - 多线程调用:事务线程和子线程不在同一个事务上下文,子线程异常不影响主线程;
- 自身调用:比如 A 类的 a () 方法调用本类的 b () 方法(b () 加了 @Transactional),因未走代理,事务失效;
- Bean 未被 Spring 管理:比如类未加 @Service/@Component,事务注解无效。
11. Spring Boot 自动配置原理?
答案 :核心是@EnableAutoConfiguration注解,流程:
- 加载自动配置类 :Spring Boot 启动时,通过
SpringFactoriesLoader读取META-INF/spring.factories文件,加载所有AutoConfiguration类; - 条件过滤 :自动配置类上的
@ConditionalOnClass(存在指定类)、@ConditionalOnMissingBean(不存在指定 Bean)等注解,过滤掉不满足条件的配置; - 注册 Bean :满足条件的自动配置类,通过
@Bean向容器注册组件(比如 DataSourceAutoConfiguration 自动配置数据源); - 配置优先级 :用户自定义配置 > 配置文件 > 自动配置(可通过
@ConditionalOnMissingBean保证用户配置优先)。
三、中间件(核心)
12. MySQL 索引失效的常见场景?
答案:
- 模糊查询以 % 开头 :
like %xxx(无法走索引,like xxx%可以); - 索引列参与计算 / 函数 :
where id + 1 = 10、where SUBSTR(name,1,1) = 'a'; - 索引列使用隐式类型转换 :比如索引列是 varchar,查询用
where id = 123(字符串转数字); - or 连接非索引列 :
where index_col = 1 or non_index_col = 2; - 联合索引不满足最左匹配 :联合索引 (a,b,c),查询
where b=1 and c=2(跳过 a,索引失效); - 数据分布导致优化器放弃索引:比如索引列值重复率高(比如性别),优化器会走全表扫描。
13. MySQL 事务的隔离级别及 MVCC 原理?
答案 :隔离级别(从低到高):
- 读未提交(Read Uncommitted):能读到其他事务未提交的数据(脏读);
- 读已提交(Read Committed):只能读到其他事务已提交的数据(解决脏读,存在不可重复读);
- 可重复读(Repeatable Read):MySQL 默认级别,同一事务内多次读取结果一致(解决不可重复读,存在幻读);
- 串行化(Serializable):最高级别,完全避免脏读 / 不可重复读 / 幻读,但性能最差。
MVCC(多版本并发控制):实现可重复读的核心机制,通过 "版本链 + undo log+ReadView" 实现:
- 版本链 :每行数据包含
trx_id(创建事务 ID)、roll_pointer(指向 undo log 的指针),修改数据时生成新版本,通过 roll_pointer 串联旧版本; - undo log:存储数据的旧版本,用于回滚或读取历史版本;
- ReadView :事务读取数据时的 "快照",包含当前活跃事务 ID 列表,判断数据版本是否可见:
- 若数据
trx_id < ReadView最小活跃ID:可见; - 若数据
trx_id > ReadView最大活跃ID:不可见; - 若在活跃 ID 范围内:不可见(未提交)。
- 若数据
14. Redis 缓存穿透、缓存击穿、缓存雪崩的区别及解决方案?
| 问题 | 定义 | 解决方案 |
|---|---|---|
| 缓存穿透 | 请求不存在的 key,穿透到 DB,DB 压力大 | 1. 空值缓存(缓存不存在的 key,设置短期过期);2. 布隆过滤器(提前过滤不存在的 key) |
| 缓存击穿 | 热点 key 过期,大量请求同时打到 DB | 1. 热点 key 永不过期;2. 互斥锁(比如 Redis 的 SETNX),只有一个线程去 DB 加载数据 |
| 缓存雪崩 | 大量 key 同时过期,或 Redis 集群宕机,DB 压垮 | 1. 过期时间加随机值(避免同时过期);2. Redis 集群部署(主从 + 哨兵);3. 降级 / 限流 |
15. Kafka 保证消息可靠性的机制?
答案:
- 生产者端 :
acks=all:消息需被所有 ISR(同步副本)确认后才返回成功;- 重试机制:
retries设置重试次数,retry.backoff.ms设置重试间隔; - 幂等性 / 事务:开启幂等性(
enable.idempotence=true)避免重复发送,事务保证批量消息的原子性。
- Broker 端 :
- 副本机制:每个分区配置多个副本(
replication.factor),ISR 列表中的副本同步数据,Leader 宕机后 Follower 升级为 Leader; - 最小 ISR 数:
min.insync.replicas设置最小同步副本数,低于该值则拒绝写入。
- 副本机制:每个分区配置多个副本(
- 消费者端 :
- 手动提交 offset(
enable.auto.commit=false):消费完成后再提交,避免消费失败但 offset 已提交; - 死信队列:处理消费失败的消息,避免重复消费。
- 手动提交 offset(
四、场景题(中高级)
16. 秒杀系统设计核心要点?
答案 :核心目标:限流、削峰、防超卖、高可用,核心步骤:
- 前端限流:按钮置灰、验证码、接口限流(比如每秒 1 次);
- 网关层:Nginx 限流(limit_req_zone)、黑白名单;
- 应用层 :
- 接口限流(Sentinel/Guava RateLimiter);
- 库存预扣减(Redis 原子操作
decr,避免超卖); - 消息队列削峰(Kafka/RabbitMQ,异步处理订单);
- 数据层 :
- 分库分表(按商品 ID 分片);
- 库存最终一致性(Redis 预扣减 + DB 最终校验,定时任务补偿);
- 兜底方案:服务降级(比如秒杀失败返回 "稍后重试")、熔断(避免服务雪崩)。
17. 如何保证缓存和数据库的数据一致性?
答案 :核心原则:最终一致性(强一致性成本高),常用方案:
- 更新策略(推荐) :先更 DB,再删缓存(而非更缓存):
- 原因:避免并发下 "更新缓存" 覆盖最新数据(比如 A 更 DB→B 更 DB→B 更缓存→A 更缓存,导致缓存是旧值);
- 补偿:定时任务扫描 DB 和缓存,修复不一致数据。
- 延迟双删:更 DB→删缓存→延迟 1 秒→再删缓存(解决删缓存前缓存已过期,新请求加载旧值的问题);
- 分布式事务:比如 Seata 的 TCC 模式(成本高,仅核心场景使用)。
总结
- 基础核心:Java 基础(JVM、多线程、集合)是所有面试的 "底线",必须吃透原理 + 场景;
- 框架重点:Spring IOC/AOP、事务失效场景,MyBatis 核心原理是高频考点;
- 中间件核心:MySQL 索引 / MVCC、Redis 缓存问题、Kafka 可靠性机制,需结合业务场景理解;
- 场景题:秒杀、缓存一致性等问题,重点答 "限流、削峰、最终一致性、兜底方案" 等核心思路。