Java 开发岗位核心面试八股文指南(2026 最新版)
本资料涵盖 Java 后端开发面试的核心知识点,适用于校招/社招准备。
最后更新: 2026 年 3 月 19 日
📑 目录
第一部分 Java 基础
1.1 面向对象三大特性
封装(Encapsulation)
- 将数据和方法捆绑在类中
- 隐藏实现细节,对外提供公共接口
- 使用访问修饰符控制可见性(private、protected、public)
继承(Inheritance)
- 子类继承父类的属性和方法
- 支持代码复用
- Java 只支持单继承,但可实现多个接口
多态(Polymorphism)
- 同一操作作用于不同对象产生不同行为
- 编译时多态:方法重载(Overload)
- 运行时多态:方法重写(Override)+ 父类引用指向子类对象
多态实现条件:
- 继承关系
- 方法重写
- 父类引用指向子类对象
// 多态示例
Animal animal = new Dog(); // 父类引用指向子类对象
animal.sound(); // 调用 Dog 类的 sound() 方法
1.2 重载 vs 重写
| 对比项 | 重载 (Overload) | 重写 (Override) |
|---|---|---|
| 位置 | 同一类中 | 子类对父类方法 |
| 方法名 | 相同 | 相同 |
| 参数列表 | 必须不同 | 必须相同 |
| 返回类型 | 无要求 | 相同或是其子类 |
| 访问修饰符 | 无要求 | 不能低于父类 |
| 异常 | 无要求 | 不能抛出更多异常 |
1.3 == 与 equals 区别
// == 比较
- 基本数据类型:比较值是否相等
- 引用数据类型:比较内存地址是否相同
// equals 方法
- 默认实现(Object 类):比较地址(同 ==)
- 重写后(如 String):比较内容是否相等
示例:
String s1 = new String("hello");
String s2 = new String("hello");
s1 == s2; // false,地址不同
s1.equals(s2); // true,内容相同
1.4 hashCode 作用及与 equals 关系
hashCode 作用:
- 哈希算法生成的整数值
- 提高集合(HashMap、HashSet)查找效率
- 先根据 hashCode 定位存储区域,再调用 equals 比较
约定:
- equals 相等的对象,hashCode 必须相等
- hashCode 相等的对象,equals 不一定相等(哈希冲突)
为什么重写 equals 必须重写 hashCode:
// 不重写 hashCode 的问题
HashSet<Person> set = new HashSet<>();
Person p1 = new Person("张三", 25);
Person p2 = new Person("张三", 25);
set.add(p1);
set.contains(p2); // 可能返回 false,因为 hashCode 不同
1.5 String、StringBuffer、StringBuilder
| 特性 | String | StringBuffer | StringBuilder |
|---|---|---|---|
| 可变性 | 不可变(final 字符数组) | 可变 | 可变 |
| 线程安全 | 安全 | 安全(synchronized) | 不安全 |
| 性能 | 低(每次创建新对象) | 中 | 高 |
| 适用场景 | 少量字符串操作 | 多线程字符串拼接 | 单线程大量拼接 |
String 为什么不可变:
- 安全性:参数传递不会被修改
- 线程安全:天然支持并发
- 缓存优化:支持字符串常量池
- 哈希码缓存:适合做 HashMap 的 key
1.6 final 关键字用法
| 修饰对象 | 含义 |
|---|---|
| 类 | 不能被继承(如 String 类) |
| 方法 | 不能被子类重写 |
| 变量 | 值不能被修改(引用不可变,内容可变) |
| 参数 | 方法内不能重新赋值 |
示例:
final List<String> list = new ArrayList<>();
list.add("hello"); // ✓ 允许,修改内容
list = new ArrayList<>(); // ✗ 不允许,修改引用
final int x = 10;
x = 20; // ✗ 不允许
JVM 优化:
- final 方法可能被内联优化
- final 常量编译时存入常量池
1.7 static 关键字用法
| 类型 | 说明 |
|---|---|
| 静态变量 | 类级别共享,所有实例共用一份 |
| 静态方法 | 可直接通过类名调用,不能访问非静态成员 |
| 静态代码块 | 类加载时执行一次,用于初始化 |
| 静态内部类 | 不依赖外部类实例,可独立创建 |
| 静态导包 | import static java.lang.Math.*; |
静态代码块执行顺序:
class Parent {
static { System.out.println("父类静态块"); }
}
class Child extends Parent {
static { System.out.println("子类静态块"); }
}
// 执行 Child.class 时:
// 1. 父类静态块
// 2. 子类静态块
1.8 try-catch-finally
执行规则:
- finally 始终执行(即使 try 有 return)
- finally 在 return 之前执行
- finally 中不要包含 return(会覆盖 try 的返回值)
示例:
public int test() {
try {
return 1;
} finally {
return 2; // 最终返回 2,不推荐这样写
}
}
资源关闭最佳实践:
// Java 7+ try-with-resources
try (FileInputStream fis = new FileInputStream("file.txt")) {
// 自动关闭资源
} catch (IOException e) {
e.printStackTrace();
}
1.9 反射机制
定义:运行时动态获取类的信息(属性、方法、构造器)并操作对象
获取 Class 对象的三种方式:
// 1. Class.forName(最常用,会初始化类)
Class<?> clazz1 = Class.forName("com.example.Person");
// 2. 类.class(不会初始化)
Class<?> clazz2 = Person.class;
// 3. 对象.getClass()
Person p = new Person();
Class<?> clazz3 = p.getClass();
反射创建对象:
Class<?> clazz = Class.forName("com.example.Person");
// 方式 1:newInstance(已废弃)
Person p1 = (Person) clazz.newInstance();
// 方式 2:获取构造器(推荐)
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
Person p2 = (Person) constructor.newInstance("张三");
反射应用场景:
- JDBC 驱动加载
- Spring IOC 容器
- 动态代理(AOP)
- 自定义注解处理
- 通用框架开发
1.10 创建对象的 5 种方式
| 方式 | 代码示例 | 是否调用构造器 |
|---|---|---|
| new 关键字 | new Person() | ✓ |
| Class.newInstance | clazz.newInstance() | ✓ |
| Constructor.newInstance | constructor.newInstance() | ✓ |
| clone 方法 | obj.clone() | ✗ |
| 反序列化 | ObjectInputStream.readObject() | ✗ |
1.11 深拷贝 vs 浅拷贝
| 类型 | 说明 | 实现方式 |
|---|---|---|
| 浅拷贝 | 只复制对象本身,引用仍指向原对象 | 实现 Cloneable 接口 |
| 深拷贝 | 复制对象及其引用的所有对象 | 序列化 + 反序列化 |
浅拷贝示例:
class Person implements Cloneable {
String name;
Address address; // 引用类型
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝,address 仍指向原对象
}
}
深拷贝示例:
// 通过序列化实现深拷贝
public Person deepClone() throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Person) ois.readObject();
}
第二部分 集合框架
2.1 Collection 体系结构
Collection(接口)
├── List(有序、可重复)
│ ├── ArrayList(动态数组)
│ ├── LinkedList(双向链表)
│ └── Vector(线程安全,已过时)
│ └── Stack(栈)
├── Set(无序、不可重复)
│ ├── HashSet(哈希表)
│ │ └── LinkedHashSet(维护插入顺序)
│ └── TreeSet(红黑树,可排序)
└── Queue(队列)
├── PriorityQueue(优先队列)
└── Deque(双端队列)
├── ArrayDeque
└── LinkedList
2.2 ArrayList vs LinkedList
| 特性 | ArrayList | LinkedList |
|---|---|---|
| 底层结构 | 动态数组 | 双向链表 |
| 随机访问 | O(1),支持 get(i) | O(n),需遍历 |
| 插入删除 | 尾部 O(1),中间 O(n) | 头尾 O(1),中间需遍历 |
| 内存占用 | 预留扩容空间 | 额外存储前后节点指针 |
| 扩容机制 | 自动扩容(1.5 倍) | 无需扩容 |
| 线程安全 | 不安全 | 不安全 |
| 适用场景 | 读多写少 | 频繁头尾插入删除 |
ArrayList 扩容机制:
// 默认容量:10
// 扩容公式:newCapacity = oldCapacity + (oldCapacity >> 1)
// 即:新容量 = 原容量 * 1.5
// 扩容代码(简化版)
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
elementData = Arrays.copyOf(elementData, newCapacity);
}
2.3 HashMap 核心原理(⭐⭐⭐⭐⭐)
底层结构
JDK 1.7:数组 + 链表(头插法) JDK 1.8:数组 + 链表/红黑树(尾插法)
HashMap 结构:
┌─────────┐
│ [0] │ ──→ Node(key1, value1) → Node(key2, value2)
├─────────┤
│ [1] │ ──→ null
├─────────┤
│ [2] │ ──→ TreeNode(key3, value3) → TreeNode(key4, value4)
└─────────┘
核心参数
| 参数 | 默认值 | 说明 |
|---|---|---|
| DEFAULT_INITIAL_CAPACITY | 16 | 默认初始容量 |
| MAXIMUM_CAPACITY | 1 << 30 | 最大容量 |
| DEFAULT_LOAD_FACTOR | 0.75f | 默认负载因子 |
| TREEIFY_THRESHOLD | 8 | 链表转红黑树阈值 |
| UNTREEIFY_THRESHOLD | 6 | 红黑树转链表阈值 |
关键方法原理
put 方法流程:
public V put(K key, V value) {
// 1. 计算 hash 值
int hash = hash(key);
int index = indexFor(hash, table.length);
// 2. 判断桶是否为空
if (table[index] == null) {
table[index] = newNode(hash, key, value, null);
} else {
// 3. 遍历链表/红黑树
for (Node<K,V> e = table[index]; e != null; e = e.next) {
// 4. key 已存在,覆盖 value
if (e.hash == hash && key.equals(e.key)) {
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
// 5. 头插法/尾插法插入新节点
addNode(table[index], hash, key, value);
}
// 6. 判断是否需要扩容
if (++size > threshold) {
resize();
}
return null;
}
hash 方法(扰动函数):
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
// 高低 16 位异或,减少哈希冲突
}
扩容机制:
// 扩容条件:size > capacity * loadFactor
// 扩容后:容量翻倍,重新 hash 分布
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int newCap = oldCap << 1; // 容量翻倍
// 重新 hash 分布
// JDK 1.8 优化:无需重新计算 hash,根据高位判断位置
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e = oldTab[j];
if (e != null) {
// 原位置 或 原位置 + oldCap
if ((e.hash & oldCap) == 0) {
newTab[j] = e;
} else {
newTab[j + oldCap] = e;
}
}
}
}
为什么负载因子是 0.75
- 时间效率与空间效率的平衡
- 根据泊松分布,负载因子 0.75 时,链表长度超过 8 的概率极低(约 0.00000006)
为什么链表长度超过 8 转红黑树
- 根据泊松分布统计,链表长度达到 8 的概率非常低
- 红黑树查找 O(log n),链表查找 O(n)
- 转换阈值 8,还原阈值 6(避免频繁转换)
2.4 ConcurrentHashMap 原理(⭐⭐⭐⭐⭐)
JDK 1.7 vs JDK 1.8
| 特性 | JDK 1.7 | JDK 1.8 |
|---|---|---|
| 数据结构 | 分段数组(Segment[]) | 数组 + 链表/红黑树 |
| 锁机制 | 分段锁(ReentrantLock) | CAS + synchronized |
| 锁粒度 | Segment 级别 | 桶(Node)级别 |
| 并发度 | Segment 数量 | 数组长度 |
JDK 1.8 实现原理
ConcurrentHashMap 结构:
┌────────────────────────────────────────┐
│ Node[] table │
├────────────────────────────────────────┤
│ [0] → Node → Node → TreeNode │
│ [1] → Node → Node │
│ [2] → null │
│ ... │
└────────────────────────────────────────┘
锁粒度:每个桶(数组元素)独立加锁
put 方法流程:
public V put(K key, V value) {
return putVal(key, value, false);
}
final V putVal(K key, V value, boolean onlyIfAbsent) {
// 1. 计算 hash
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
// 2. 桶为空,CAS 插入
if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, new Node<>(hash, key, value)))
break;
}
// 3. 正在扩容,帮助扩容
else if ((fh = f.hash) == MOVED)
helpTransfer(tab, f);
else {
// 4. 桶不为空,synchronized 锁头节点
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
// 链表遍历
for (Node<K,V> e = f;; ++binCount) {
if (e.hash == hash && key.equals(e.key)) {
// key 存在,覆盖
oldVal = e.val;
e.val = value;
break;
}
if (e.next == null) {
e.next = new Node<>(hash, key, value, null);
break;
}
}
} else if (f instanceof TreeBin) {
// 红黑树插入
Node<K,V> p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value);
if (p != null) {
oldVal = p.val;
p.val = value;
}
}
}
}
if (binCount != 0) {
// 5. 判断是否转红黑树
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
// 6. 更新 size
addCount(1L, binCount);
return null;
}
如何保证线程安全
- CAS 操作:无锁插入空桶
- synchronized:锁链表/红黑树头节点
- volatile:保证可见性
- 分段思想:不同桶可并发操作
2.5 HashMap vs HashTable
| 特性 | HashMap | HashTable |
|---|---|---|
| 父类 | AbstractMap | Dictionary |
| null 支持 | key 可为 null(1 个),value 可为 null | key/value 都不能为 null |
| 线程安全 | 不安全 | 安全(synchronized) |
| 效率 | 高 | 低 |
| 迭代器 | fail-fast | fail-fast |
| 推荐替代 | - | ConcurrentHashMap |
2.6 Collection vs Collections
| 名称 | 类型 | 说明 |
|---|---|---|
| Collection | 接口 | 集合体系的顶层接口(Set、List 的父接口) |
| Collections | 工具类 | 提供集合操作的静态方法 |
Collections 常用方法:
// 排序
Collections.sort(list);
// 二分查找
Collections.binarySearch(list, key);
// 反转
Collections.reverse(list);
// 线程安全化
Collections.synchronizedList(list);
// 空列表
Collections.emptyList();
第三部分 并发编程
3.1 线程与进程
| 概念 | 说明 |
|---|---|
| 程序 | 静态的指令和数据文件 |
| 进程 | 程序的一次执行过程,资源分配的基本单位 |
| 线程 | 进程的更小执行单位,CPU 调度的基本单位 |
关系:
- 一个进程可包含多个线程
- 线程共享进程的堆和方法区
- 每个线程有独立的程序计数器、虚拟机栈、本地方法栈
3.2 线程创建方式
方式 1:继承 Thread 类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行");
}
}
new MyThread().start();
方式 2:实现 Runnable 接口
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程执行");
}
}
new Thread(new MyRunnable()).start();
方式 3:实现 Callable 接口(有返回值)
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 100;
}
}
FutureTask<Integer> task = new FutureTask<>(new MyCallable());
new Thread(task).start();
Integer result = task.get(); // 获取返回值
方式 4:线程池(推荐)
ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -> System.out.println("线程执行"));
executor.shutdown();
3.3 线程状态
NEW → RUNNABLE ↔ WAITING/TIMED_WAITING → BLOCKED → TERMINATED
NEW:新建,未启动
RUNNABLE:可运行(包括就绪和运行中)
BLOCKED:等待获取锁
WAITING:无限期等待(wait/join/park)
TIMED_WAITING:限期等待(sleep/wait/ park)
TERMINATED:终止
状态转换:
// NEW → RUNNABLE
thread.start();
// RUNNABLE → BLOCKED
synchronized 锁竞争失败
// RUNNABLE → WAITING
object.wait();
thread.join();
LockSupport.park();
// RUNNABLE → TIMED_WAITING
Thread.sleep(1000);
object.wait(1000);
LockSupport.parkNanos(1000);
// WAITING/BLOCKED → RUNNABLE
object.notify()/notifyAll();
获取到锁
LockSupport.unpark(thread);
// RUNNABLE → TERMINATED
run() 方法执行完成
3.4 并发编程三要素
原子性(Atomicity)
- 操作要么全部执行,要么都不执行
- 保证:synchronized、Lock、原子类
可见性(Visibility)
- 一个线程修改共享变量,其他线程立即可见
- 保证:volatile、synchronized、Lock
有序性(Ordering)
- 程序执行顺序符合预期
- 保证:volatile(禁止指令重排)、happens-before 原则
3.5 volatile 关键字
作用:
- 保证可见性
- 禁止指令重排
- 不保证原子性
内存语义:
- 写 volatile 变量:刷新到主内存
- 读 volatile 变量:从主内存读取
使用场景:
// 状态标记
private volatile boolean running = true;
// 双重检查锁(DCL)单例
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // volatile 防止重排
}
}
}
return instance;
}
3.6 synchronized 原理
锁升级过程(JDK 1.6+):
无锁 → 偏向锁 → 轻量级锁 → 重量级锁
| 锁状态 | 适用场景 | 实现方式 |
|---|---|---|
| 无锁 | 无竞争 | 无同步 |
| 偏向锁 | 单线程访问 | 偏向第一个获取锁的线程 |
| 轻量级锁 | 轻度竞争 | CAS 自旋 |
| 重量级锁 | 激烈竞争 | 操作系统互斥量 |
使用方式:
// 修饰实例方法(锁当前对象)
public synchronized void method() {}
// 修饰静态方法(锁 Class 对象)
public static synchronized void method() {}
// 修饰代码块(锁指定对象)
public void method() {
synchronized (this) {
// 临界区
}
}
3.7 ReentrantLock vs synchronized
| 特性 | synchronized | ReentrantLock |
|---|---|---|
| 实现层面 | JVM 层面 | API 层面 |
| 锁释放 | 自动释放 | 手动释放(finally 中) |
| 等待可中断 | 否 | 是(lockInterruptibly) |
| 公平锁 | 否 | 支持(构造参数指定) |
| 多条件等待 | 单一条件(wait/notify) | 多个 Condition |
| 性能 | JDK 1.6+ 优化后相当 | 高竞争下略优 |
ReentrantLock 使用:
ReentrantLock lock = new ReentrantLock(true); // 公平锁
lock.lock();
try {
// 临界区
} finally {
lock.unlock(); // 必须手动释放
}
// 可中断等待
lock.lockInterruptibly();
// 尝试获取锁
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
// 获取锁成功
} finally {
lock.unlock();
}
}
3.8 线程池核心参数(⭐⭐⭐⭐⭐)
ThreadPoolExecutor 构造方法:
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 工作队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
核心参数详解
| 参数 | 说明 | 推荐配置 |
|---|---|---|
| corePoolSize | 核心线程数,线程池维持的最小线程数 | CPU 密集型:CPU 核心数 +1 IO 密集型:2*CPU 核心数 |
| maximumPoolSize | 最大线程数 | 根据任务类型调整 |
| keepAliveTime | 非核心线程空闲超时时间 | 默认 60 秒 |
| workQueue | 任务队列 | ArrayBlockingQueue(有界) LinkedBlockingQueue(无界) |
| threadFactory | 线程创建工厂 | 自定义线程名,便于排查 |
| handler | 拒绝策略 | 根据业务需求选择 |
工作流程
新任务提交
↓
核心线程数未满 → 创建核心线程执行
↓
核心线程已满 → 加入工作队列
↓
队列已满 → 创建非核心线程
↓
达到最大线程数 → 执行拒绝策略
拒绝策略
| 策略 | 说明 |
|---|---|
| AbortPolicy | 抛出 RejectedExecutionException(默认) |
| CallerRunsPolicy | 调用者线程执行 |
| DiscardPolicy | 直接丢弃 |
| DiscardOldestPolicy | 丢弃最老任务,执行新任务 |
线程池使用示例
ThreadPoolExecutor executor = new ThreadPoolExecutor(
8, // 核心线程数
16, // 最大线程数
60, // 空闲存活时间
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(100), // 有界队列
new ThreadFactoryBuilder() // 自定义线程工厂
.setNameFormat("pool-%d")
.build(),
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
executor.submit(() -> System.out.println("任务执行"));
executor.shutdown();
3.9 ThreadLocal 原理
作用:线程本地变量,每个线程有独立的变量副本
应用场景:
- 数据库连接管理
- Session 管理
- SimpleDateFormat 线程安全
原理:
// 每个 Thread 内部维护 ThreadLocalMap
class Thread {
ThreadLocalMap threadLocals;
}
// ThreadLocalMap 存储键值对
// key:ThreadLocal 对象(弱引用)
// value:线程本地变量值
内存泄漏问题:
- key 是弱引用,value 是强引用
- ThreadLocal 被回收后,value 仍无法回收
- 解决:使用完调用
remove()方法
private static final ThreadLocal<SimpleDateFormat> formatter =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
try {
String date = formatter.get().format(new Date());
} finally {
formatter.remove(); // 必须清理
}
3.10 AQS(AbstractQueuedSynchronizer)
核心思想:
- 使用 volatile int state 表示同步状态
- 使用 CLH 队列管理等待线程
核心方法:
// 获取同步状态
protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); }
// 释放同步状态
protected boolean tryRelease(int arg) { throw new UnsupportedOperationException(); }
// 等待队列
private transient volatile Node head;
private transient volatile Node tail;
基于 AQS 实现的锁:
- ReentrantLock
- CountDownLatch
- CyclicBarrier
- Semaphore
- ReentrantReadWriteLock
3.11 并发工具类
CountDownLatch(倒计时门闩)
CountDownLatch latch = new CountDownLatch(3);
// 子线程
new Thread(() -> {
// 执行任务
latch.countDown(); // 计数减 1
}).start();
// 主线程等待
latch.await(); // 等待计数为 0
System.out.println("所有任务完成");
CyclicBarrier(循环屏障)
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("所有线程到达屏障");
});
// 线程中
barrier.await(); // 等待其他线程
Semaphore(信号量)
Semaphore semaphore = new Semaphore(5); // 5 个许可
semaphore.acquire(); // 获取许可
try {
// 执行任务
} finally {
semaphore.release(); // 释放许可
}
第四部分 JVM
4.1 JVM 内存区域(⭐⭐⭐⭐⭐)
┌─────────────────────────────────────────────────────┐
│ JVM 内存结构 │
├─────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 堆(Heap) │ │
│ │ - 对象实例 │ │
│ │ - 垃圾回收主要区域 │ │
│ │ - 新生代(Eden + Survivor) │ │
│ │ - 老年代 │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 方法区 │ │ 虚拟机栈 │ │ 本地方法栈 │ │
│ │ (元空间) │ │ │ │ │ │
│ │ - 类信息 │ │ - 栈帧 │ │ - Native │ │
│ │ - 常量池 │ │ - 局部变量 │ │ 方法 │ │
│ │ - 静态变量 │ │ - 操作数栈 │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 程序计数器 (PC Register) │ │
│ │ - 记录当前执行的字节码指令地址 │ │
│ └──────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────┘
各区域详解
| 区域 | 作用 | 线程共享 | OOM 异常 |
|---|---|---|---|
| 堆 | 存储对象实例 | 共享 | 是 |
| 方法区 | 类信息、常量、静态变量 | 共享 | 是 |
| 虚拟机栈 | Java 方法执行(栈帧) | 私有 | 是(StackOverflowError) |
| 本地方法栈 | Native 方法执行 | 私有 | 是 |
| 程序计数器 | 记录字节码执行位置 | 私有 | 否 |
4.2 堆内存结构
堆内存(Heap)
├── 新生代(Young Generation)
│ ├── Eden 区(8/10)
│ └── Survivor 区(2/10)
│ ├── From
│ └── To
└── 老年代(Old Generation)
对象分配流程:
- 新对象优先分配在 Eden 区
- Eden 满时触发 Minor GC
- 存活对象复制到 Survivor 区
- 年龄达到阈值(默认 15)晋升老年代
- 大对象直接进入老年代
4.3 垃圾回收算法
| 算法 | 原理 | 优点 | 缺点 | 适用区域 |
|---|---|---|---|---|
| 标记 - 清除 | 标记可回收对象,统一清除 | 简单 | 内存碎片 | 老年代 |
| 复制 | 存活对象复制到另一区域 | 无碎片 | 空间利用率低 | 新生代 |
| 标记 - 整理 | 标记后向一端整理 | 无碎片 | 效率低 | 老年代 |
| 分代收集 | 根据对象生命周期分代 | 高效 | 复杂 | 全堆 |
4.4 垃圾收集器
| 收集器 | 特点 | 适用场景 |
|---|---|---|
| Serial | 单线程,STW | 客户端模式 |
| ParNew | Serial 多线程版 | 新生代 |
| Parallel Scavenge | 关注吞吐量 | 后台计算 |
| CMS | 低延迟,标记清除 | Web 应用 |
| G1 | 分区回收,可预测停顿 | 大堆内存 |
| ZGC | 超低停顿(<10ms) | JDK 11+,大堆 |
G1 收集器特点:
- 将堆划分为多个 Region
- 可预测停顿时间
- 整体标记整理,局部复制
4.5 类加载机制
类加载过程
加载 → 验证 → 准备 → 解析 → 初始化
| 阶段 | 说明 |
|---|---|
| 加载 | 读取字节码文件,生成 Class 对象 |
| 验证 | 验证字节码合法性 |
| 准备 | 为静态变量分配内存并设默认值 |
| 解析 | 符号引用转为直接引用 |
| 初始化 | 执行静态代码块和静态变量赋值 |
双亲委派模型
Bootstrap ClassLoader
↑
Extension ClassLoader
↑
Application ClassLoader
↑
自定义 ClassLoader
委派过程:
- 收到类加载请求
- 先委托给父加载器
- 父加载器无法加载,子加载器才尝试
优点:
- 保证核心类安全(防止篡改)
- 避免重复加载
破坏双亲委派:
- SPI 机制(JDBC、JNDI)
- 热部署(OSGi)
- 代码热替换
4.6 JVM 调优参数
常用参数
| 参数 | 说明 |
|---|---|
-Xms | 初始堆大小 |
-Xmx | 最大堆大小 |
-Xmn | 新生代大小 |
-XX:MetaspaceSize | 元空间初始大小 |
-XX:MaxMetaspaceSize | 元空间最大大小 |
-XX:+UseG1GC | 使用 G1 收集器 |
-XX:MaxGCPauseMillis | 最大 GC 停顿时间 |
-XX:+PrintGCDetails | 打印 GC 日志 |
-Xloggc:/path/gc.log | GC 日志路径 |
调优步骤
- 监控 GC 日志
- 分析工具:gceasy.io、GCViewer
- 调整堆大小和收集器
- 压测验证
第五部分 Spring 框架
5.1 Spring IOC(控制反转)
核心概念:
- 将对象创建权交给 Spring 容器
- 通过依赖注入(DI)实现
注入方式:
// 构造器注入(推荐)
@Component
public class UserService {
private final UserRepository repo;
@Autowired
public UserService(UserRepository repo) {
this.repo = repo;
}
}
// Setter 注入
@Component
public class UserService {
private UserRepository repo;
@Autowired
public void setRepo(UserRepository repo) {
this.repo = repo;
}
}
// 字段注入(不推荐)
@Component
public class UserService {
@Autowired
private UserRepository repo;
}
常用注解:
| 注解 | 说明 |
|---|---|
@Component | 通用组件 |
@Service | 服务层 |
@Repository | 数据访问层 |
@Controller | 控制层 |
@Autowired | 按类型注入 |
@Resource | 按名称注入 |
@Qualifier | 配合@Autowired 按名称 |
5.2 Spring AOP(面向切面编程)
核心概念:
- 切面(Aspect):横切关注点的模块化
- 连接点(Joinpoint):可插入切面的点
- 通知(Advice):切面在连接点的动作
- 切入点(Pointcut):匹配连接点的表达式
- 织入(Weave):将切面应用到目标对象
通知类型:
@Aspect
@Component
public class LogAspect {
@Before("execution(* com.example.service.*.*(..))")
public void before(JoinPoint jp) {
// 前置通知
}
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void afterReturning(Object result) {
// 返回后通知
}
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "e")
public void afterThrowing(Exception e) {
// 异常通知
}
@After("execution(* com.example.service.*.*(..))")
public void after() {
// 最终通知
}
@Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 环绕通知
return pjp.proceed();
}
}
应用场景:
- 日志记录
- 事务管理
- 权限控制
- 性能监控
5.3 Spring 事务管理
事务传播行为
| 传播行为 | 说明 |
|---|---|
| REQUIRED | 加入当前事务,无则新建(默认) |
| REQUIRES_NEW | 总是新建事务,挂起当前事务 |
| NESTED | 嵌套事务 |
| SUPPORTS | 支持当前事务,无则以非事务执行 |
| NOT_SUPPORTED | 以非事务执行,挂起当前事务 |
| MANDATORY | 必须存在事务,否则抛异常 |
| NEVER | 必须非事务,否则抛异常 |
事务隔离级别
| 级别 | 说明 | 问题 |
|---|---|---|
| DEFAULT | 使用数据库默认 | - |
| READ_UNCOMMITTED | 读未提交 | 脏读、不可重复读、幻读 |
| READ_COMMITTED | 读已提交 | 不可重复读、幻读 |
| REPEATABLE_READ | 可重复读 | 幻读 |
| SERIALIZABLE | 串行化 | 无 |
事务失效场景
- 方法非 public
- 同类内部调用(自调用)
- 异常被捕获未抛出
- 数据库不支持事务
- 异常类型不匹配
5.4 Spring Bean 生命周期
实例化 → 属性赋值 → 初始化 → 使用 → 销毁
详细流程:
- 实例化 Bean
- 属性注入
- 调用 Aware 接口方法
- BeanPostProcessor 前置处理
- 调用初始化方法(@PostConstruct、InitializingBean)
- BeanPostProcessor 后置处理
- Bean 就绪,可使用
- 容器关闭时调用销毁方法(@PreDestroy、DisposableBean)
5.5 Spring Boot 自动装配
核心注解:
@SpringBootApplication
= @SpringBootConfiguration
+ @EnableAutoConfiguration
+ @ComponentScan
自动装配原理:
@EnableAutoConfiguration导入 AutoConfigurationImportSelector- 读取
META-INF/spring.factories中的自动配置类 - 根据条件注解(@Conditional)判断是否生效
- 加载符合条件的配置类
自定义 Starter:
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService() {
return new MyService();
}
}
第六部分 MySQL 数据库
6.1 MySQL 架构
┌─────────────────────────────────────────────────────┐
│ MySQL 架构 │
├─────────────────────────────────────────────────────┤
│ 连接层:连接管理、权限验证 │
│ ↓ │
│ 服务层:SQL 解析、优化、缓存 │
│ ↓ │
│ 引擎层:存储引擎(InnoDB、MyISAM) │
│ ↓ │
│ 存储层:数据存储、索引 │
└─────────────────────────────────────────────────────┘
6.2 存储引擎对比
| 特性 | InnoDB | MyISAM |
|---|---|---|
| 事务 | 支持 | 不支持 |
| 锁粒度 | 行锁 | 表锁 |
| 外键 | 支持 | 不支持 |
| 崩溃恢复 | 支持 | 不支持 |
| COUNT(*) | 全表扫描 | 内置计数器 |
| 适用场景 | 事务、高并发 | 读多写少 |
6.3 索引原理(⭐⭐⭐⭐⭐)
B+ 树结构
┌─────────┐
│ 17,35 │ 非叶子节点(只存索引)
└────┬────┘
│
┌────────┼────────┐
↓ ↓ ↓
┌───────┐ ┌───────┐ ┌───────┐
│1,5,10 │ │20,25,30│ │40,45,50│ 叶子节点(存数据)
└───────┘ └───────┘ └───────┘
↑ ↑ ↑
数据行 数据行 数据行
B+ 树特点:
- 非叶子节点只存索引,不存数据
- 叶子节点存所有数据,且有指针相连
- 查询效率稳定,O(log n)
聚簇索引 vs 非聚簇索引
| 类型 | 说明 | InnoDB |
|---|---|---|
| 聚簇索引 | 数据与索引在一起 | 主键索引 |
| 非聚簇索引 | 索引与数据分离 | 二级索引 |
回表查询:
- 二级索引查到主键
- 再用主键查聚簇索引获取完整数据
覆盖索引:
- 查询的列都在索引中,无需回表
6.4 索引优化原则
最左前缀原则
-- 联合索引 (a, b, c)
WHERE a = 1 AND b = 2 AND c = 3 -- ✓ 使用索引
WHERE a = 1 AND b = 2 -- ✓ 使用索引
WHERE a = 1 -- ✓ 使用索引
WHERE b = 2 AND c = 3 -- ✗ 不使用索引
WHERE a = 1 AND c = 3 -- △ 只使用 a 列索引
索引失效场景
| 场景 | 示例 | 说明 |
|---|---|---|
| 函数操作 | WHERE DATE(create_time) = '2024-01-01' | 索引失效 |
| 类型转换 | WHERE phone = 13800000000(phone 是字符串) | 隐式转换,索引失效 |
| 模糊查询 | WHERE name LIKE '%张%' | 前缀通配符,索引失效 |
| OR 条件 | WHERE a = 1 OR b = 2 | b 无索引则全表扫描 |
| NOT/!= | WHERE age != 18 | 可能全表扫描 |
索引优化建议
- 为高频查询字段创建索引
- 使用覆盖索引减少回表
- 避免 SELECT *
- 大表分页优化:
WHERE id > 1000 LIMIT 10 - 定期分析慢查询日志
6.5 事务隔离级别
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置隔离级别
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | ✓ | ✓ | ✓ |
| READ COMMITTED | ✗ | ✓ | ✓ |
| REPEATABLE READ(MySQL 默认) | ✗ | ✗ | ✗* |
| SERIALIZABLE | ✗ | ✗ | ✗ |
*MySQL 通过 MVCC 和 Next-Key Lock 解决幻读
6.6 MVCC(多版本并发控制)
实现原理:
- 每行数据有隐藏列(事务 ID、回滚指针)
- 读操作读取历史版本
- 写操作创建新版本
Read View:
- 当前活跃事务列表
- 最小活跃事务 ID
- 最大事务 ID
可见性判断:
事务 ID < 最小活跃 ID → 可见
事务 ID >= 最大活跃 ID → 不可见
最小 <= 事务 ID < 最大 → 检查是否在活跃列表
6.7 锁机制
行锁类型
| 锁类型 | 说明 |
|---|---|
| 记录锁 | 锁住索引记录 |
| 间隙锁 | 锁住索引间隙,防止幻读 |
| 临键锁 | 记录锁 + 间隙锁 |
死锁排查
-- 查看死锁日志
SHOW ENGINE INNODB STATUS;
-- 查看锁等待
SELECT * FROM information_schema.innodb_lock_waits;
6.8 SQL 优化
EXPLAIN 分析
EXPLAIN SELECT * FROM users WHERE id = 1;
| 字段 | 说明 |
|---|---|
| type | 访问类型(ALL < index < range < ref < eq_ref < const < system) |
| key | 实际使用的索引 |
| rows | 扫描行数 |
| Extra | 额外信息(Using index 覆盖索引,Using filesort 文件排序) |
优化案例
-- 优化前:全表扫描
SELECT * FROM orders WHERE YEAR(create_time) = 2024;
-- 优化后:使用索引
SELECT * FROM orders
WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01';
-- 分页优化
SELECT * FROM users LIMIT 1000000, 10; -- 慢
SELECT * FROM users WHERE id > 1000000 LIMIT 10; -- 快
第七部分 Redis 缓存
7.1 数据结构与应用场景
| 数据结构 | 应用场景 |
|---|---|
| String | 缓存、计数器、分布式锁 |
| Hash | 对象存储、购物车 |
| List | 消息队列、最新列表 |
| Set | 去重、好友关系 |
| ZSet | 排行榜、优先级队列 |
| Bitmap | 签到、状态标记 |
| HyperLogLog | UV 统计 |
| Geo | 地理位置 |
7.2 持久化机制
RDB(Redis Database)
| 优点 | 缺点 |
|---|---|
| 紧凑的二进制文件 | 可能丢失最后一次快照后的数据 |
| 恢复速度快 | 无法实时持久化 |
| 适合备份 | 大数据量时 fork 耗时 |
触发方式:
- 手动:
SAVE、BGSAVE - 自动:满足配置条件(save 900 1、save 300 10、save 60 10000)
AOF(Append Only File)
| 优点 | 缺点 |
|---|---|
| 数据更安全 | 文件体积大 |
| 可配置每秒 fsync | 恢复速度慢 |
| 重写压缩 | - |
重写机制:
- 合并多条命令
- 忽略无效命令(如已删除的 key)
推荐配置
# RDB + AOF 混合持久化
save 900 1
save 300 10
save 60 10000
appendonly yes
appendfsync everysec
7.3 缓存问题及解决方案(⭐⭐⭐⭐⭐)
缓存穿透
问题:查询不存在的数据,请求直达数据库
解决方案:
- 布隆过滤器:拦截不存在的 key
- 缓存空值:设置较短过期时间
- 接口层校验:参数合法性检查
// 布隆过滤器
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("bloomFilter");
bloomFilter.tryInit(1000000L, 0.03); // 预期 100 万,误差率 3%
if (!bloomFilter.contains(key)) {
return null; // 拦截
}
缓存击穿
问题:热点 key 过期,大量请求直达数据库
解决方案:
- 互斥锁:只让一个线程查数据库
- 永不过期:逻辑过期,异步更新
// 互斥锁方案
public String getData(String key) {
String value = redis.get(key);
if (value == null) {
if (tryLock("lock:" + key)) {
try {
value = redis.get(key);
if (value == null) {
value = fetchFromDB();
redis.setex(key, 3600, value);
}
} finally {
unlock("lock:" + key);
}
} else {
Thread.sleep(50);
return getData(key); // 重试
}
}
return value;
}
缓存雪崩
问题:大量 key 同时过期,请求直达数据库
解决方案:
- 随机过期时间:基础时间 + 随机值
- 高可用集群:多节点分散压力
- 限流降级:保护数据库
// 随机过期时间
int expireTime = 3600 + new Random().nextInt(300); // 3600~3900 秒
redis.setex(key, expireTime, value);
7.4 分布式锁
Redis 实现
// 加锁
String result = redis.set(lockKey, requestId, "NX", "PX", 30000);
if ("OK".equals(result)) {
try {
// 业务逻辑
} finally {
// 解锁(Lua 脚本)
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
}
}
Redisson 实现(推荐)
RLock lock = redisson.getLock("lockKey");
lock.lock(); // 自动续期(看门狗)
try {
// 业务逻辑
} finally {
lock.unlock();
}
// 尝试加锁
if (lock.tryLock(1, 10, TimeUnit.SECONDS)) {
try {
// 业务逻辑
} finally {
lock.unlock();
}
}
看门狗机制:
- 默认 30 秒过期
- 每 10 秒自动续期
- 解锁后停止续期
7.5 缓存一致性
方案对比
| 方案 | 说明 | 适用场景 |
|---|---|---|
| 先删缓存再更新 DB | 可能读到旧数据 | 不推荐 |
| 先更新 DB 再删缓存 | Cache Aside Pattern | 推荐 |
| 延迟双删 | 删→更新→sleep→删 | 高一致性要求 |
| 监听 Binlog | Canal 同步删除 | 最终一致性 |
Cache Aside Pattern
// 读
public Data read(String key) {
Data data = redis.get(key);
if (data == null) {
data = db.query(key);
redis.setex(key, 3600, data);
}
return data;
}
// 写
public void write(String key, Data data) {
db.update(key, data);
redis.del(key); // 删除缓存而非更新
}
7.6 Redis 集群
主从复制
- 一主多从
- 读写分离
- 数据备份
哨兵模式
- 监控主从节点
- 自动故障转移
- 高可用
Redis Cluster
- 分片存储(16384 个 slot)
- 去中心化
- 自动故障转移
slot 分配:
CRC16(key) % 16384 = slot 编号
第八部分 分布式与微服务
8.1 分布式事务
解决方案对比
| 方案 | 特点 | 适用场景 |
|---|---|---|
| 2PC | 强一致性,性能差 | 不推荐 |
| TCC | Try-Confirm-Cancel,补偿机制 | 高一致性要求 |
| 本地消息表 | 最终一致性,简单可靠 | 推荐 |
| RocketMQ 事务消息 | 半消息机制 | 推荐 |
| Seata | AT 模式,无侵入 | 推荐 |
Seata AT 模式原理
- 一阶段:执行 SQL,记录 undo_log
- 提交:释放本地锁
- 回滚:根据 undo_log 反向补偿
8.2 分布式 ID
生成方案
| 方案 | 优点 | 缺点 |
|---|---|---|
| UUID | 简单 | 无序,不适合索引 |
| 数据库自增 | 有序 | 单点,性能低 |
| 号段模式 | 性能高 | 依赖 DB |
| 雪花算法 | 有序,高性能 | 时钟回拨问题 |
雪花算法结构
64 位 ID = 符号位 (1) + 时间戳 (41) + 机器 ID (10) + 序列号 (12)
8.3 微服务组件
| 组件 | 功能 | 常见实现 |
|---|---|---|
| 注册中心 | 服务注册与发现 | Nacos、Eureka、Consul |
| 配置中心 | 配置管理 | Nacos、Apollo |
| 网关 | 路由、鉴权、限流 | Gateway、Zuul |
| 负载均衡 | 流量分发 | Ribbon、LoadBalancer |
| 熔断降级 | 故障隔离 | Sentinel、Hystrix |
| 链路追踪 | 调用链监控 | SkyWalking、Zipkin |
8.4 限流算法
| 算法 | 原理 | 实现 |
|---|---|---|
| 计数器 | 单位时间计数 | 简单,临界问题 |
| 滑动窗口 | 细分时间片 | 平滑限流 |
| 漏桶 | 固定速率流出 | 平滑,无法应对突发 |
| 令牌桶 | 固定速率放入令牌 | 可应对突发流量 |
Sentinel 限流:
@SentinelResource(value = "getUser", blockHandler = "blockHandler")
public User getUser(String id) {
return userService.getUser(id);
}
public User blockHandler(String id, BlockException e) {
return new User("限流中...");
}
第九部分 系统设计
9.1 高并发设计
核心思路
分流 → 缓存 → 异步 → 降级 → 限流
分层设计
| 层级 | 策略 |
|---|---|
| 接入层 | DNS 轮询、CDN、负载均衡 |
| 网关层 | 路由、鉴权、限流 |
| 服务层 | 无状态、水平扩展 |
| 缓存层 | 多级缓存、热点隔离 |
| 数据层 | 分库分表、读写分离 |
9.2 秒杀系统设计
核心挑战
- 瞬时高并发
- 超卖问题
- 黄牛刷单
解决方案
┌─────────────────────────────────────────────────────┐
│ 秒杀系统架构 │
├─────────────────────────────────────────────────────┤
│ │
│ 用户 → CDN 静态资源 │
│ ↓ │
│ 网关层:限流、黑名单、验证码 │
│ ↓ │
│ Redis 预减库存(原子操作) │
│ ↓ │
│ MQ 异步下单(削峰) │
│ ↓ │
│ 数据库扣减库存(乐观锁) │
│ │
└─────────────────────────────────────────────────────┘
关键代码
// Redis 预减库存
String stockKey = "seckill:stock:" + productId;
Long stock = redis.decr(stockKey);
if (stock < 0) {
redis.incr(stockKey);
return Result.error("库存不足");
}
// MQ 异步下单
seckillQueue.send(order);
// 数据库乐观锁
UPDATE stock SET count = count - 1
WHERE product_id = ? AND count > 0;
9.3 短链接系统设计
核心流程
- 生成短码(MD5/雪花算法/Base62)
- 建立映射关系(短码 → 原 URL)
- 重定向(301/302)
短码生成
// Base62 编码
String shortCode = Base62.encode(snowflakeId());
// MD5 截取
String md5 = MD5(url + salt);
String shortCode = md5.substring(0, 8);
第十部分 高频面试题速查
10.1 Java 基础
| 问题 | 关键词 |
|---|---|
| String 为什么不可变 | final 字符数组、安全性、缓存 |
| == 与 equals 区别 | 地址 vs 内容 |
| hashCode 与 equals | 约定、集合查找 |
| final、finally、finalize | 修饰符、异常、GC |
| 重载与重写 | 同类 vs 父子类 |
| 深拷贝与浅拷贝 | 引用是否复制 |
| 反射原理及应用 | Class 对象、动态代理 |
| 创建对象的方式 | new、反射、clone、序列化 |
10.2 集合框架
| 问题 | 关键词 |
|---|---|
| ArrayList vs LinkedList | 数组 vs 链表、随机访问 |
| HashMap 底层原理 | 数组 + 链表/红黑树、扩容 |
| HashMap 线程安全吗 | 不安全、ConcurrentHashMap |
| ConcurrentHashMap 原理 | CAS + synchronized、分段 |
| HashMap vs HashTable | null 支持、线程安全 |
| ArrayList 扩容机制 | 1.5 倍、copy |
| HashMap 为什么用红黑树 | O(log n)、阈值 8 |
10.3 并发编程
| 问题 | 关键词 |
|---|---|
| 线程创建方式 | Thread、Runnable、Callable、线程池 |
| 线程状态及转换 | NEW、RUNNABLE、BLOCKED、WAITING |
| volatile 原理 | 可见性、禁止重排、不保证原子性 |
| synchronized 原理 | 锁升级、Monitor |
| ReentrantLock vs synchronized | API vs JVM、公平锁 |
| 线程池参数及流程 | corePoolSize、workQueue、拒绝策略 |
| ThreadLocal 原理及泄漏 | 线程本地变量、弱引用、remove |
| AQS 原理 | state、CLH 队列 |
| CAS 原理及 ABA 问题 | 乐观锁、版本号 |
10.4 JVM
| 问题 | 关键词 |
|---|---|
| JVM 内存区域 | 堆、栈、方法区、程序计数器 |
| 垃圾回收算法 | 标记清除、复制、标记整理 |
| 垃圾收集器 | CMS、G1、ZGC |
| 类加载机制 | 双亲委派、破坏场景 |
| JVM 调优参数 | -Xms、-Xmx、G1GC |
| OOM 排查 | heap dump、MAT |
| 对象分配过程 | Eden、Survivor、老年代 |
10.5 Spring
| 问题 | 关键词 |
|---|---|
| IOC 原理 | 控制反转、依赖注入 |
| AOP 原理及应用 | 动态代理、切面、通知 |
| Bean 生命周期 | 实例化、初始化、销毁 |
| 事务传播行为 | REQUIRED、REQUIRES_NEW |
| 事务隔离级别 | 脏读、不可重复读、幻读 |
| Spring Boot 自动装配 | spring.factories、条件注解 |
| 循环依赖解决 | 三级缓存 |
10.6 MySQL
| 问题 | 关键词 |
|---|---|
| 索引底层结构 | B+ 树、聚簇索引 |
| 索引优化原则 | 最左前缀、覆盖索引 |
| 索引失效场景 | 函数、类型转换、模糊查询 |
| 事务隔离级别 | READ COMMITTED、REPEATABLE READ |
| MVCC 原理 | 隐藏列、Read View |
| 锁机制 | 行锁、间隙锁、临键锁 |
| SQL 优化 | EXPLAIN、慢查询 |
| 分库分表 | 水平拆分、垂直拆分 |
10.7 Redis
| 问题 | 关键词 |
|---|---|
| 数据结构及应用 | String、Hash、ZSet |
| 持久化机制 | RDB、AOF |
| 缓存穿透/击穿/雪崩 | 布隆过滤器、互斥锁、随机过期 |
| 分布式锁实现 | setnx、Redisson、看门狗 |
| 缓存一致性 | Cache Aside、延迟双删 |
| Redis 集群 | 主从、哨兵、Cluster |
| 热点 key 处理 | 本地缓存、拆分 |
10.8 分布式
| 问题 | 关键词 |
|---|---|
| 分布式事务方案 | TCC、本地消息表、Seata |
| 分布式 ID 生成 | 雪花算法、号段模式 |
| 限流算法 | 令牌桶、漏桶、滑动窗口 |
| 微服务组件 | 注册中心、配置中心、网关 |
| CAP 理论 | 一致性、可用性、分区容错 |
| BASE 理论 | 基本可用、软状态、最终一致 |
附录
A. 复习优先级
第一梯队(必会):
✅ HashMap/ConcurrentHashMap
✅ 线程池
✅ synchronized/volatile
✅ MySQL 索引
✅ Redis 缓存问题
✅ Spring 事务
第二梯队(高频):
✅ JVM 内存模型
✅ 垃圾回收
✅ Spring IOC/AOP
✅ 分布式锁
✅ 分布式事务
第三梯队(加分):
✅ JVM 调优
✅ 系统设计
✅ 高并发方案
B. 面试技巧
- STAR 法则:Situation、Task、Action、Result
- 由浅入深:先说概念,再讲原理,最后举例
- 引导话题:主动提及自己熟悉的领域
- 诚实原则:不会的承认,表示愿意学习
C. 推荐资源
- JavaGuide: javaguide.cn
- 小林 coding: 图解计算机基础
- 极客时间: Java 并发、JVM 实战
- LeetCode: 算法刷题
文档版本:2026.03
最后更新:2026 年 3 月 19 日
祝面试顺利!🎉