在 Java 后端面试中,面试官往往聚焦于候选人对核心技术的理解深度、实战经验以及问题排查能力。本文将从JVM、集合框架、并发编程、Spring 生态、数据库五大高频领域,梳理面试中的核心考点,并结合典型问题与解析,帮助求职者系统化梳理知识,从容应对面试挑战。

完整版场景题已打包:DDD面试指南

一、JVM:面试中的 “基础门槛”

JVM 是 Java 跨平台特性的核心,也是面试中最易拉开差距的模块。面试官不仅会考察理论知识,更会结合 “内存溢出排查”“性能优化” 等实际场景提问。

1.1 核心考点:JVM 内存模型(JDK 8+)

JDK 8 取消了永久代,引入元空间(Metaspace),内存模型主要分为以下区域,需重点区分 “线程私有” 与 “线程共享”:

  • 线程私有区域(随线程创建 / 销毁):
    • 程序计数器:记录当前线程执行字节码的行号,唯一不会 OOM 的区域;
    • 虚拟机栈:存储方法调用的栈帧(局部变量表、操作数栈等),栈深度不足会抛StackOverflowError,内存不足会抛OutOfMemoryError;
    • 本地方法栈:与虚拟机栈类似,为 Native 方法服务。
  • 线程共享区域(整个 JVM 进程共享):
    • 堆(Heap):存储对象实例,垃圾回收(GC)的核心区域,分为年轻代(Eden + Survivor)和老年代;
    • 元空间(Metaspace):存储类元信息(类结构、方法信息等),默认使用本地内存,避免了永久代 OOM 问题。

面试高频问题:“JDK 8 为什么取消永久代,改用元空间?”

参考回答:① 永久代大小难确定,容易因类加载过多导致 OOM;② 元空间使用本地内存,可动态扩展,减少内存溢出风险;③ 便于 HotSpot 与其他 JVM(如 JRockit)融合。

1.2 垃圾回收(GC):从 “理论” 到 “实战”

GC 是 JVM 面试的重中之重,需掌握 “回收算法”“垃圾收集器”“GC 日志分析” 三大模块。

(1)垃圾判定算法

  • 引用计数法:通过对象引用数判断是否可回收,无法解决循环引用问题(如 A 引用 B,B 引用 A);
  • 可达性分析:以 “GC Roots”(如虚拟机栈引用的对象、静态变量引用的对象)为起点,遍历对象引用链,不可达的对象标记为可回收。

(2)常见垃圾收集器(JDK 8 + 主流)

需明确不同收集器的适用场景,避免死记硬背:

收集器

适用区域

特点

适用场景

Serial

年轻代

单线程回收,暂停时间长

客户端应用、小型程序

Parallel Scavenge

年轻代

多线程回收,追求高吞吐量

服务器端批量处理任务

CMS(Concurrent Mark Sweep)

老年代

并发回收,低暂停时间

互联网应用(如 Web 服务)

G1(Garbage-First)

整堆

分区回收,兼顾吞吐量与低延迟

大内存场景(如 16G + 堆内存)

面试高频问题:“CMS 收集器的工作流程,以及可能出现的问题?”

参考回答:流程分为 4 步:① 初始标记(STW,标记 GC Roots 直接引用的对象);② 并发标记(与用户线程并行,遍历引用链);③ 重新标记(STW,修正并发标记的偏差);④ 并发清除(与用户线程并行,回收垃圾)。

可能问题:① 并发清除阶段产生 “浮动垃圾”(新生成的垃圾无法回收);② 内存碎片(标记 - 清除算法导致,需通过 “CMS Full GC” 整理碎片,暂停时间长)。

(3)GC 实战:内存溢出排查

面试官常问 “如何排查 OOM 问题”,需掌握工具与流程:

  1. 开启 JVM 参数:添加-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heapdump.hprof,OOM 时自动生成堆转储文件;
  1. 分析工具:使用 JDK 自带的jhat或可视化工具(如 MAT、VisualVM),查看对象引用链,定位内存泄漏点(如静态集合未清理、线程池核心线程持有大对象);
  1. 常见场景:① ArrayList 无限添加元素导致堆 OOM;② 递归调用过深导致栈 OOM;③ 类加载过多(如动态生成类)导致元空间 OOM。

二、集合框架:从 “使用” 到 “原理”

集合框架是 Java 开发的基础,面试中不仅会考察 API 使用,更会深入底层数据结构与源码实现(如 HashMap、ConcurrentHashMap)。

2.1 核心集合体系

需明确 Collection 与 Map 两大接口的继承关系,避免混淆:

  • Collection:存储单值元素,主要子类包括 List(有序、可重复)、Set(无序、不可重复)、Queue(队列,FIFO);
  • Map:存储键值对(Key-Value),主要实现类包括 HashMap、TreeMap、ConcurrentHashMap。

2.2 高频考点:HashMap(JDK 8)

HashMap 是面试中 “必问” 知识点,需掌握数据结构、扩容机制、线程安全性三大核心。

(1)底层数据结构

JDK 8 中,HashMap 采用 “数组 + 链表 + 红黑树” 的结构:

  • 数组(哈希桶):存储链表 / 红黑树的头节点,数组长度默认 16(必须是 2 的幂,便于通过(n-1)&hash计算索引);
  • 链表:当哈希冲突时,元素以链表形式存储,链表长度超过 8 且数组长度≥64 时,转为红黑树(提升查询效率,从 O (n) 到 O (logn));
  • 红黑树:当链表长度小于 6 时,红黑树转回链表(避免红黑树维护成本过高)。

(2)扩容机制

  • 触发条件:当元素数量(size)超过 “负载因子(默认 0.75)× 数组长度” 时,触发扩容,数组长度翻倍;
  • 扩容核心:重新计算每个元素的哈希索引((newCap -1)&hash),并迁移元素(JDK 8 优化了迁移逻辑,避免重复计算哈希)。

(3)线程安全性

HashMap 线程不安全,并发场景下可能出现 “链表环”(JDK 7)或数据覆盖问题。解决方式:

  • 替换为ConcurrentHashMap(JDK 8 使用 CAS+synchronized 实现,效率高于 JDK 7 的分段锁);
  • 使用Collections.synchronizedMap(new HashMap<>())(全局锁,效率低)。

面试高频问题:“JDK 7 与 JDK 8 中 HashMap 的区别?”

参考回答:① 数据结构:JDK 7 是 “数组 + 链表”,JDK 8 是 “数组 + 链表 + 红黑树”;② 哈希计算:JDK 7 多次扰动哈希(减少冲突),JDK 8 简化为 “hashCode ^ (hashCode >>> 16)”;③ 扩容迁移:JDK 7 头插法(易产生链表环),JDK 8 尾插法(避免环);④ 初始化:JDK 7 在构造函数中初始化数组,JDK 8 在第一次 put 时初始化。

三、并发编程:面试中的 “难点与重点”

并发编程是 Java 后端开发的核心能力,面试官会围绕 “线程池、锁机制、并发容器” 展开,考察候选人对多线程问题的理解与解决能力。

3.1 线程池:从 “参数” 到 “实战选型”

线程池是并发编程的 “利器”,也是面试高频考点,需掌握 “核心参数、工作原理、拒绝策略”。

(1)核心参数(ThreadPoolExecutor)

public ThreadPoolExecutor(

int corePoolSize, // 核心线程数(常驻线程)

int maximumPoolSize, // 最大线程数(核心+临时线程)

long keepAliveTime, // 临时线程空闲时间

TimeUnit unit, // 时间单位

BlockingQueue<Runnable> workQueue, // 任务队列

ThreadFactory threadFactory, // 线程工厂(自定义线程名)

RejectedExecutionHandler handler // 拒绝策略

)

(2)工作原理

  1. 提交任务时,优先创建核心线程执行;
  1. 核心线程满后,任务放入工作队列;
  1. 队列满后,创建临时线程执行;
  1. 临时线程满后,触发拒绝策略。

(3)拒绝策略

需根据业务场景选择合适的拒绝策略:

  • AbortPolicy(默认):直接抛RejectedExecutionException,中断业务;
  • CallerRunsPolicy:由提交任务的线程执行(如主线程),减缓任务提交速度;
  • DiscardPolicy:默默丢弃任务,无任何提示;
  • DiscardOldestPolicy:丢弃队列中最旧的任务,尝试提交新任务。

面试高频问题:“如何设计线程池的核心参数?”

参考回答:需结合业务类型(CPU 密集型 / IO 密集型)判断:

  • CPU 密集型(如计算任务):核心线程数 = CPU 核心数 + 1(减少线程切换开销);
  • IO 密集型(如数据库查询、HTTP 请求):核心线程数 = CPU 核心数 × 2(线程大部分时间在等待 IO,可多创建线程提高利用率);
  • 队列选择:无界队列(如 LinkedBlockingQueue)适合任务量稳定场景,避免拒绝;有界队列(如 ArrayBlockingQueue)适合任务量波动大场景,防止内存溢出。

3.2 锁机制:从 “synchronized” 到 “Lock”

锁是解决并发安全的核心,需掌握synchronized与Lock的区别,以及 “乐观锁”“悲观锁” 的应用场景。

(1)synchronized:从 “重量级锁” 到 “优化”

JDK 6 对synchronized进行了优化,引入 “偏向锁、轻量级锁、重量级锁” 三级锁状态,减少性能开销:

  • 偏向锁:单线程场景下,避免锁竞争,直接标记线程 ID;
  • 轻量级锁:多线程交替执行,通过 CAS 尝试获取锁,避免进入内核态;
  • 重量级锁:多线程竞争激烈,通过操作系统互斥量(Mutex)实现,会导致线程阻塞(上下文切换开销大)。

(2)Lock 接口(ReentrantLock)

ReentrantLock是synchronized的补充,支持更多特性:

  • 可中断锁:通过lockInterruptibly()中断等待锁的线程;
  • 可超时锁:通过tryLock(long time, TimeUnit unit)避免无限等待;
  • 公平锁:通过构造函数new ReentrantLock(true)实现,按线程等待顺序获取锁(默认非公平锁,效率更高);
  • 条件变量:通过newCondition()实现多线程间的通信(如生产者 - 消费者模型)。

面试高频问题:“synchronized 与 ReentrantLock 的区别?”

参考回答:① 锁实现:synchronized 是 JVM 层面的锁(字节码指令monitorenter/monitorexit),ReentrantLock 是 Java 层面的锁(基于 AQS);② 特性:ReentrantLock 支持可中断、可超时、公平锁,synchronized 不支持;③ 释放锁:synchronized 自动释放(异常或方法结束),ReentrantLock 需手动释放(finally中调用unlock());④ 性能:JDK 6 后两者性能接近,简单场景用 synchronized,复杂场景用 ReentrantLock。

四、Spring 生态:后端开发的 “基石”

Spring 是 Java 后端开发的主流框架,面试中会围绕 “Spring IoC、Spring AOP、Spring Boot、Spring Cloud” 展开,考察候选人对框架设计思想与实战应用的理解。

4.1 Spring IoC:控制反转与依赖注入

IoC(Inversion of Control)是 Spring 的核心思想,即 “将对象的创建与依赖管理交给 Spring 容器”,避免硬编码耦合。

(1)依赖注入(DI)的三种方式

  • 构造器注入:通过构造函数注入依赖,强制依赖必须传入(推荐,避免空指针);
  • Setter 注入:通过 setter 方法注入依赖,适合可选依赖;
  • 字段注入:通过@Autowired直接注入字段,代码简洁但不便于测试(不推荐)。

(2)Bean 的作用域与生命周期

  • 作用域:默认singleton(单例,整个容器一个实例),其他包括prototype(每次获取新实例)、request(每个 HTTP 请求一个实例)、session(每个会话一个实例);
  • 生命周期:① 实例化(调用构造函数);② 属性注入(设置依赖);③ 初始化(@PostConstruct或InitializingBean);④ 销毁(@PreDestroy或DisposableBean)。

面试高频问题:“Spring Bean 的循环依赖如何解决?”

参考回答:循环依赖指 A 依赖 B,B 依赖 A。Spring 通过 “三级缓存” 解决单例 Bean 的循环依赖:

  • 一级缓存:存储已初始化完成的 Bean;
  • 二级缓存:存储已实例化但未注入依赖的 Bean;
  • 三级缓存:存储 Bean 的工厂对象(用于生成代理对象)。

流程:A 实例化后放入三级缓存,注入 B 时发现 B 未创建,B 实例化后放入三级缓存,注入 A 时从三级缓存获取 A 的工厂对象,生成 A 的代理对象放入二级缓存,B 初始化完成后放入一级缓存,再回到 A 注入 B,A 初始化完成后放入一级缓存。

注意:prototypeBean 不支持循环依赖(每次获取新实例,无法缓存),会抛BeanCurrentlyInCreationException。

4.2 Spring AOP:面向切面编程

AOP(Aspect-Oriented Programming)是 Spring 的另一核心,通过 “横切关注点”(如日志、事务、权限)的提取,实现代码解耦。

(1)核心概念

  • 切面(Aspect):横切关注点的封装(如@Aspect注解的类);
  • 通知(Advice):切面的具体逻辑(如@Before、@After、@Around);
  • 连接点(JoinPoint):程序执行的某个位置(如方法调用、字段赋值);
  • 切入点(Pointcut):筛选连接点的表达式(如execution(* com.xxx.service.*.*(..)));
  • 目标对象(Target):被代理的原始对象。

(2)实现原理

Spring AOP 基于动态代理实现,分为两种方式:

  • JDK 动态代理:基于接口,代理类实现目标接口,通过Proxy.newProxyInstance()生成代理对象;
  • CGLIB 动态代理:基于类,代理类继承目标类,通过 ASM 字节码框架生成代理对象。

规则:如果目标对象实现了接口,默认用 JDK 动态代理;否则用 CGLIB。可通过proxyTargetClass=true强制使用 CGLIB。

面试高频问题:“Spring 事务的传播机制有哪些?如何实现的?”

参考回答:事务传播机制定义了多个事务方法嵌套调用时,事务的行为(如是否新建事务、是否加入现有事务),核心传播机制包括:

  • REQUIRED(默认):如果当前有事务,加入;否则新建事务;
  • REQUIRES_NEW:无论当前是否有事务,都新建事务(原有事务挂起);
  • SUPPORTS:如果当前有事务,加入;否则以非事务方式执行;
  • NOT_SUPPORTED:以非事务方式执行,原有事务挂起;
  • NEVER:以非事务方式执行,存在事务则抛异常;
  • MANDATORY:必须在事务中执行,无事务则抛异常;
  • NESTED:如果当前有事务,在嵌套事务中执行(独立提交 / 回滚);否则新建事务。

实现原理:Spring 事务基于 AOP,通过TransactionInterceptor拦截目标方法,在方法执行前开启事务,执行后提交 / 回滚事务(异常时),底层依赖TransactionManager(如DataSourceTransactionManager)与数据库事务(JDBC 的Connection)。

五、数据库:从 “SQL 优化” 到 “事务隔离”

数据库是后端开发的 “数据存储核心”,面试中会围绕 “SQL 优化、索引、事务隔离级别、分库分表” 展开,考察候选人的数据存储与性能优化能力。

5.1 索引:提升查询效率的 “关键”

索引是数据库优化的核心,需掌握 “索引类型、索引原理、失效场景”。

(1)索引类型

  • 按数据结构:B + 树索引(InnoDB 默认,适合范围查询)、Hash 索引(适合等值查询,不支持范围)、全文索引(如 Elasticsearch 中用于文本检索);
  • 按功能:主键索引(唯一且非空,InnoDB 中主键索引即聚簇索引)、唯一索引(值唯一,允许空)、普通索引(无约束)、联合索引(多列组合,需遵循 “最左前缀原则”)。

(2)索引原理(InnoDB 聚簇索引)

  • InnoDB 中,聚簇索引(主键索引)是核心,数据与索引存储在一起,叶节点存储完整的行数据;非聚簇索引(如普通索引、唯一索引)的叶节点存储主键值,查询时需通过主键值回表查询完整数据(即 “回表” 操作)。

    示例:若表 user 主键为 id,有普通索引 name,执行 select * from user where name = '张三' 时:

  • 先通过 name 索引找到对应的主键值 id=10;
  • 再通过 id 聚簇索引找到 id=10 对应的完整行数据。

(3)索引失效场景(面试高频)

索引失效会导致全表扫描,需重点规避以下场景:

  • 使用函数或表达式操作索引列:如 where substring(name, 1, 1) = '张'(可用 where name like '张%' 替代);
  • 索引列使用不等于(!=、<>)、not in、is not null:会导致索引失效,转向全表扫描;
  • 字符串未加引号:如 where name = 123(name 为字符串类型,会触发类型转换,导致索引失效);
  • 联合索引不满足最左前缀原则:如联合索引 (name, age),查询 where age = 20 会导致索引失效;
  • like 以 % 开头:如 where name like '%三'(无法利用索引,若为 like '张%' 则可利用索引)。

面试高频问题:“什么是覆盖索引?如何避免回表?”

解析:覆盖索引指查询的列均可通过索引获取,无需回表。例如:

  • 表 user 有联合索引 (name, age),执行 select name, age from user where name = '张三' 时,可直接通过联合索引获取 name 和 age,无需回表;
  • 避免回表的核心是:让查询的列包含在索引中(如设计合适的联合索引),减少 “回表” 带来的性能开销。

5.2 SQL 优化:从 “慢查询” 到 “高效执行”

SQL 优化是数据库面试的核心,需掌握 “慢查询分析流程”“优化技巧” 两大模块。

(1)慢查询分析流程

  • 开启慢查询日志:在 MySQL 配置文件中添加 slow_query_log = 1(开启慢查询)、long_query_time = 1(执行时间超过 1 秒的查询记录到日志)、slow_query_log_file = /var/lib/mysql/slow.log(日志存储路径);
  • 分析慢查询日志:使用工具 mysqldumpslow(如 mysqldumpslow -s c -t 10 /var/lib/mysql/slow.log,查看执行次数最多的前 10 条慢查询)或可视化工具(如 Navicat 慢查询分析);
  • 查看执行计划:通过 explain 分析 SQL 执行计划,重点关注 type(索引类型)、key(实际使用的索引)、rows(预估扫描行数)、Extra(额外信息,如 “Using index”“Using filesort”)。
  • (2)SQL 优化核心技巧

  • 优化查询语句:避免 select *(只查询需要的列,便于利用覆盖索引)、拆分复杂查询(如将多表关联的大查询拆分为多个小查询);
  • 优化表结构:合理设计字段类型(如用 int 代替 varchar 存储 ID、用 datetime 代替 varchar 存储时间)、避免冗余字段(通过关联查询获取数据,减少数据不一致风险);
  • 优化索引:删除冗余索引(如联合索引 (name, age) 已包含 name 单列索引的功能,无需重复创建 name 索引)、避免过度索引(索引会增加插入 / 更新 / 删除的开销);
  • 处理分页查询:大数据量分页(如 limit 100000, 10)会导致全表扫描,可通过主键过滤优化:
-- 优化前(全表扫描,效率低)
select * from user limit 100000, 10;
-- 优化后(利用主键索引,效率高)
select * from user where id > 100000 limit 10;

5.3 事务隔离级别:解决 “并发一致性问题”

数据库事务需满足 ACID 特性(原子性、一致性、隔离性、持久性),而隔离性是解决并发场景下 “脏读、不可重复读、幻读” 问题的核心。

(1)并发一致性问题

  • 脏读:一个事务读取到另一个事务未提交的数据(如事务 A 修改了数据但未提交,事务 B 读取到该未提交数据,之后事务 A 回滚,事务 B 读取的数据为 “脏数据”);
  • 不可重复读:同一事务内,多次读取同一数据,结果不一致(如事务 A 第一次读取数据为 100,事务 B 修改数据为 200 并提交,事务 A 再次读取数据为 200);
  • 幻读:同一事务内,多次执行同一查询,返回的结果集行数不一致(如事务 A 查询 “age> 20” 的用户有 10 条,事务 B 插入一条 “age=25” 的用户并提交,事务 A 再次查询 “age > 20” 的用户有 11 条)。

(2)MySQL 事务隔离级别(InnoDB 支持)

MySQL 支持 4 种隔离级别,默认隔离级别为 Repeatable Read(可重复读),不同级别解决的一致性问题不同:

隔离级别

脏读

不可重复读

幻读

实现原理

Read Uncommitted(读未提交)

允许

允许

允许

无锁,直接读取数据

Read Committed(读已提交)

禁止

允许

允许

行级锁,读取已提交的数据

Repeatable Read(可重复读)

禁止

禁止

禁止

行级锁 + MVCC(多版本并发控制)

Serializable(串行化)

禁止

禁止

禁止

表级锁,事务串行执行,性能最低

面试高频问题:“InnoDB 的 Repeatable Read 级别如何解决幻读?”

参考回答:InnoDB 通过 MVCC(多版本并发控制) 解决幻读:

  • MVCC 为每行数据添加隐藏列(DB_TRX_ID 事务 ID、DB_ROLL_PTR 回滚指针),通过回滚日志维护数据的多个版本;
  • 事务启动时生成一个 “Read View”(读视图),包含当前活跃事务的 ID 列表,查询时只读取 “Read View” 之前提交的版本数据;
  • 即使其他事务插入新数据并提交,当前事务的 “Read View” 未变化,查询时仍读取旧版本数据,从而避免幻读。

5.4 分库分表:解决 “大数据量存储” 问题

当单表数据量超过 1000 万或单库数据量超过 100GB 时,会导致查询缓慢、索引失效,需通过分库分表拆分数据。

(1)分库分表方案

  • 水平拆分(按行拆分):将同一表的数据按规则拆分到多个表 / 库中,每个表 / 库的结构相同。
  1. 拆分规则:按范围(如按 id 分,1-10000 存表 1,10001-20000 存表 2)、按哈希(如 id % 4 分 4 个表)、按时间(如按月份分表,user_202401、user_202402);
  2. 适用场景:单表数据量过大,需分散存储压力。
  • 垂直拆分(按列拆分):将同一表的不同列拆分到多个表 / 库中,每个表 / 库存储部分列数据。
  1. 拆分规则:按业务关联性拆分(如表 user 拆分为 user_base(存储基本信息:id、name、age)和 user_extend(存储扩展信息:avatar、address));
  2. 适用场景:表字段过多,部分字段访问频率低(如 avatar 访问频率低,拆分后可减少查询时的数据传输量)。
  3. (2)分库分表工具

  • 客户端工具:Sharding-JDBC(轻量级,基于 JDBC 扩展,嵌入应用中,无需额外部署服务);
  • 中间件工具:MyCat(基于 Proxy 模式,独立部署服务,应用通过 MyCat 访问数据库,支持更多高级特性如读写分离、容灾)。

(3)分库分表挑战(面试高频)

  • 分布式事务:跨库 / 跨表操作需保证事务一致性,常用方案有 2PC(两阶段提交,如 Seata AT 模式)、TCC(Try-Confirm-Cancel)、SAGA 模式;
  • 跨库查询:如查询 “所有表中 age> 20 的用户”,需通过工具聚合多表结果(如 Sharding-JDBC 的联邦查询);
  • 全局 ID:分库分表后需保证 ID 唯一,常用方案有 UUID(无序,不利于索引)、雪花算法(Snowflake,生成有序 ID,包含时间戳、机器 ID、序列号)、数据库自增(如单独用一个库的表生成自增 ID)。

面试高频问题:“分库分表后,如何实现全局唯一 ID?”

参考回答:推荐使用雪花算法(Snowflake),其结构如下(64 位 Long 类型):

  • 1 位符号位:固定为 0(表示正数);
  • 41 位时间戳:记录毫秒级时间(可使用约 69 年);
  • 10 位机器 ID:可部署 1024 台机器;
  • 12 位序列号:每台机器每秒可生成 4096 个 ID;

雪花算法生成的 ID 有序,便于索引,且无需依赖数据库,性能高,是分库分表中全局 ID 的首选方案。

六、面试实战:从 “知识” 到 “落地”

掌握核心技术后,还需通过实战演练提升面试表现,以下为高频场景的应对策略:

6.1 项目经验梳理:STAR 法则

面试中 “项目经验” 是重点,需用 STAR 法则清晰表达:

  • S(Situation):项目背景(如 “为解决电商平台订单查询缓慢问题,需优化订单系统”);
  • T(Task):你的任务(如 “负责订单表分库分表设计与实现,以及慢查询优化”);
  • A(Action):具体行动(如 “使用 Sharding-JDBC 按订单创建时间分表,优化 3 条慢查询 SQL,添加 2 个联合索引”);
  • R(Result):结果与数据(如 “订单查询时间从 500ms 降至 50ms 以内,支持日均 100 万订单存储”)。

6.2 技术问题应对:“原理 + 场景 + 优化”

回答技术问题时,需从 “原理” 到 “实际场景” 再到 “优化方案” 展开,避免只说理论:

  • 示例:被问 “HashMap 线程不安全,如何解决?”
  1. 原理:HashMap 并发时会出现数据覆盖(如 JDK 8)或链表环(JDK 7);
  2. 场景:若为高并发读、低并发写场景,可使用 ConcurrentHashMap;若为单线程或低并发场景,用 HashMap 即可;
  3. 优化:ConcurrentHashMap JDK 8 用 CAS+synchronized 替代 JDK 7 的分段锁,性能更高,可优先选择。

6.3 问题排查:“工具 + 流程 + 结论”

面试官常问 “如何排查线上问题”,需结合工具与流程回答:

  • 示例:“线上应用出现内存溢出(OOM),如何排查?”
  1. 工具:JDK 自带 jps(查看进程 ID)、jmap(生成堆转储文件:jmap -dump:format=b,file=heap.hprof 12345)、MAT(分析堆文件);
  2. 流程:① 开启 JVM 参数 -XX:+HeapDumpOnOutOfMemoryError 自动生成堆文件;② 用 MAT 打开堆文件,查看 “Leak Suspects”(内存泄漏疑点);③ 定位大对象引用链(如静态集合未清理);
  3. 结论:如 “发现缓存工具类中的静态 Map 未设置过期时间,导致对象堆积,修改为定时清理后 OOM 问题解决”。

七、总结

Java 后端面试核心围绕 “JVM、集合框架、并发编程、Spring 生态、数据库” 五大领域,需做到:

  • 理解原理:不仅要记住结论,更要掌握底层逻辑(如 HashMap 红黑树转换的条件、Spring 三级缓存解决循环依赖的流程);
  • 结合场景:技术需落地到实际场景(如线程池参数设计需区分 CPU 密集型与 IO 密集型);
  • 实战能力:掌握问题排查工具与流程(如 GC 日志分析、慢查询优化、OOM 排查);

以上面试题打包好的:DDD面试指南

通过系统化梳理知识、结合实战演练,才能在面试中脱颖而出,拿到理想的 Offer。

 

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐