通用 · 2026 年 3 月 2 日 0

java面试

📚 Java基础面试题

1. 面向对象特性

  • 封装:隐藏内部实现,提供公共访问方法
  • 继承:子类继承父类的属性和方法
  • 多态:同一操作作用于不同对象产生不同执行结果
  • 抽象:隐藏复杂实现细节,只展示功能定义

2. String、StringBuffer、StringBuilder区别

java

String str = "hello"; // 不可变,每次修改创建新对象
StringBuffer sb = new StringBuffer(); // 线程安全,效率较低
StringBuilder sbd = new StringBuilder(); // 线程不安全,效率最高

3. == 和 equals() 区别

  • ==:比较基本类型值,或引用类型的内存地址
  • equals():默认比较地址,可重写为比较内容

4. Java集合框架

text

Collection
├── List (有序可重复)
│   ├── ArrayList (数组结构)
│   ├── LinkedList (双向链表)
│   └── Vector (线程安全)
├── Set (无序唯一)
│   ├── HashSet (哈希表)
│   └── TreeSet (红黑树)
└── Queue (队列)
Map
├── HashMap (非线程安全)
├── Hashtable (线程安全)
└── ConcurrentHashMap (分段锁)

🚀 JVM面试题

1. JVM内存区域

  • 程序计数器:线程私有,记录当前线程执行位置
  • 虚拟机栈:存储局部变量表、操作数栈等
  • 本地方法栈:为Native方法服务
  • :所有线程共享,存储对象实例
  • 方法区:存储类信息、常量、静态变量等

2. 垃圾回收算法

  • 标记-清除:先标记后清除,产生碎片
  • 复制算法:内存对半分,适合新生代
  • 标记-整理:标记后整理,适合老年代
  • 分代收集:新生代复制,老年代标记整理

3. 类加载机制

text

加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载

⚡ 并发编程面试题

1. 线程创建方式

java

// 方式1:继承Thread
class MyThread extends Thread {
    public void run() {}
}

// 方式2:实现Runnable
class MyRunnable implements Runnable {
    public void run() {}
}

// 方式3:实现Callable
class MyCallable implements Callable<String> {
    public String call() {}
}

2. synchronized 和 Lock 区别

特性synchronizedLock
使用方式关键字接口
锁释放自动手动
中断响应不支持支持
公平性非公平可设置

3. volatile 关键字

  • 保证可见性
  • 禁止指令重排序
  • 不保证原子性

🏗️ Spring框架面试题

1. IOC和AOP

  • IOC:控制反转,将对象创建权交给容器
  • DI:依赖注入,运行时动态注入依赖
  • AOP:面向切面编程,实现横切关注点

2. Bean生命周期

text

实例化 → 属性赋值 → 初始化 → 使用 → 销毁

3. Spring Boot 自动配置

java

@SpringBootApplication
包含了:
- @Configuration:标记配置类
- @EnableAutoConfiguration:启用自动配置
- @ComponentScan:组件扫描

🗃️ 数据库面试题

1. MySQL索引

  • B+树索引:最常用,支持范围查询
  • 哈希索引:精确查询快,不支持范围
  • 全文索引:文本搜索

2. 事务特性(ACID)

  • 原子性:要么全成功,要么全失败
  • 一致性:事务前后数据完整
  • 隔离性:事务间互不干扰
  • 持久性:提交后永久保存

3. 隔离级别

级别脏读不可重复读幻读
读未提交
读已提交
可重复读
串行化

🔧 常用框架面试题

1. MyBatis

xml

<!-- #{} 和 ${} 区别 -->
#{}: 预编译,防止SQL注入
${}: 直接拼接,有SQL注入风险

2. Redis

  • 数据类型:String、Hash、List、Set、ZSet
  • 持久化:RDB(快照)、AOF(日志)
  • 缓存问题:穿透、击穿、雪崩

📝 高频场景题

1. 单例模式实现

java

public class Singleton {
    private static volatile Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

2. HashMap实现原理

  • 数组+链表+红黑树
  • 扰动函数减少碰撞
  • 扩容机制:2的幂次

💡 面试技巧

  1. STAR原则回答问题
    • Situation:背景
    • Task:任务
    • Action:行动
    • Result:结果
  2. 代码规范体现专业性
    • 命名规范
    • 异常处理
    • 注释清晰
  3. 主动展示亮点
    • 性能优化经验
    • 问题解决案例
    • 技术钻研深度

需要我详细展开某个部分吗?或者你有特定的面试目标(如初级、中级、高级)?

都展开说一下,我最近要面试需要复习

好的,我来详细展开每个部分,帮助你全面复习Java面试。

📚 一、Java基础(深度展开)

1. 面向对象三大特性详解

封装

java

public class User {
    // 私有属性,隐藏内部状态
    private String name;
    private int age;
    private List<String> permissions;
    
    // 提供公共访问方法
    public String getName() {
        return name;
    }
    
    // 可以在setter中添加校验逻辑
    public void setAge(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("年龄不合法");
        }
        this.age = age;
    }
    
    // 提供业务方法,不直接暴露内部集合
    public void addPermission(String permission) {
        if (permissions == null) {
            permissions = new ArrayList<>();
        }
        // 可以添加权限校验逻辑
        permissions.add(permission);
    }
    
    // 返回不可修改的集合,保护内部数据
    public List<String> getPermissions() {
        return Collections.unmodifiableList(permissions);
    }
}

继承的细节

java

// 抽象类:可以有抽象方法和具体方法
public abstract class Animal {
    protected String name;
    
    // 抽象方法:子类必须实现
    public abstract void makeSound();
    
    // 具体方法:子类可以继承
    public void eat() {
        System.out.println(name + "正在吃东西");
    }
    
    // final方法:子类不能重写
    public final void sleep() {
        System.out.println("睡觉中...");
    }
}

// 继承的关键点
public class Dog extends Animal {
    // 必须实现抽象方法
    @Override
    public void makeSound() {
        System.out.println("汪汪汪");
    }
    
    // 可以重写父类方法
    @Override
    public void eat() {
        super.eat(); // 调用父类方法
        System.out.println("狗啃骨头");
    }
    
    // 不能重写final方法
    // public void sleep() {} // 编译错误
}

多态的三种形式

java

public class PolymorphismDemo {
    public static void main(String[] args) {
        // 1. 编译时多态(方法重载)
        Calculator calc = new Calculator();
        calc.add(1, 2);           // 两个整数
        calc.add(1.0, 2.0);        // 两个浮点数
        calc.add(1, 2, 3);         // 三个整数
        
        // 2. 运行时多态(方法重写)
        Animal animal1 = new Dog();
        Animal animal2 = new Cat();
        animal1.makeSound(); // 输出:汪汪汪
        animal2.makeSound(); // 输出:喵喵喵
        
        // 3. 参数多态(泛型)
        List<String> stringList = new ArrayList<>();
        List<Integer> intList = new ArrayList<>();
    }
}

class Calculator {
    // 方法重载:参数个数不同
    public int add(int a, int b) {
        return a + b;
    }
    
    // 方法重载:参数类型不同
    public double add(double a, double b) {
        return a + b;
    }
    
    // 方法重载:参数个数不同
    public int add(int a, int b, int c) {
        return a + b + c;
    }
}

2. String深度解析

java

public class StringDeepDive {
    public static void main(String[] args) {
        // 1. 字符串常量池
        String s1 = "hello";
        String s2 = "hello";
        String s3 = new String("hello");
        String s4 = s3.intern(); // 手动入池
        
        System.out.println(s1 == s2);      // true(同一个常量池对象)
        System.out.println(s1 == s3);      // false(堆中新对象)
        System.out.println(s1 == s4);      // true(返回常量池对象)
        
        // 2. 字符串拼接优化
        String str1 = "a" + "b" + "c";     // 编译期优化为"abc"
        String str2 = "a";
        String str3 = str2 + "b";           // 运行时使用StringBuilder
        
        // 3. 循环中拼接字符串的正确方式
        // 错误方式:每次循环创建StringBuilder
        String result = "";
        for (int i = 0; i < 1000; i++) {
            result += i;  // 每次都会创建新对象
        }
        
        // 正确方式
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 1000; i++) {
            sb.append(i);
        }
        String correct = sb.toString();
        
        // 4. String不可变性的好处
        // - 线程安全:可以无锁共享
        // - 缓存hash值:String的hashCode只计算一次
        // - 字符串常量池:复用对象节省内存
    }
}

3. 集合框架源码级理解

HashMap源码解析

java

public class HashMapSourceAnalysis {
    // 1. 重要参数
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 16
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    static final int TREEIFY_THRESHOLD = 8;  // 链表转红黑树阈值
    static final int UNTREEIFY_THRESHOLD = 6; // 红黑树转链表阈值
    
    // 2. put方法流程
    /*
     * 1. 计算key的hash值:key.hashCode() ^ (key.hashCode() >>> 16)
     * 2. 如果table为空,进行resize()初始化
     * 3. 计算索引:i = (n - 1) & hash
     * 4. 如果位置为空,直接插入
     * 5. 如果不为空,判断key是否相等
     * 6. 如果是树节点,调用红黑树插入
     * 7. 如果是链表,遍历查找,如果找到则替换,否则在链表尾部插入
     * 8. 插入后检查链表长度是否超过8,超过转红黑树
     * 9. 检查size是否超过threshold,超过则resize()扩容
     */
    
    // 3. get方法流程
    /*
     * 1. 计算hash值
     * 2. 计算索引
     * 3. 检查第一个节点,如果key相等则返回
     * 4. 如果不相等,检查是树节点还是链表节点
     * 5. 树节点调用树的查找方法
     * 6. 链表节点遍历查找
     */
    
    // 4. 扩容机制
    /*
     * 1. 新容量 = 旧容量 * 2
     * 2. 创建新数组,容量为旧数组2倍
     * 3. 重新计算索引:元素在新数组的位置要么是原位置,要么是原位置+旧容量
     * 4. 因为hash & oldCap == 0的判断
     */
}

// ConcurrentHashMap源码特点
public class ConcurrentHashMapFeatures {
    /*
     * JDK7:分段锁机制
     * - 内部使用Segment数组,每个Segment继承ReentrantLock
     * - 默认16个Segment,支持16个线程并发写入
     * 
     * JDK8:CAS + synchronized
     * - 抛弃分段锁,使用CAS + synchronized实现
     * - 锁粒度更细:只锁链表或红黑树的头节点
     * - 并发度更高
     * 
     * 重要方法:
     * - put:先尝试CAS插入,失败则synchronized锁头节点
     * - get:不加锁,使用volatile保证可见性
     * - size:使用CounterCell数组降低竞争
     */
}

🚀 二、JVM深度解析

1. JVM内存区域详解

java

public class JVMMemoryAreas {
    /*
     * 1. 程序计数器(线程私有)
     * - 记录当前线程执行的字节码指令地址
     * - 唯一不会OOM的区域
     * 
     * 2. Java虚拟机栈(线程私有)
     * - 每个方法调用创建一个栈帧
     * - 栈帧包含:局部变量表、操作数栈、动态链接、方法出口
     * - StackOverflowError:栈深度超过限制
     * - OutOfMemoryError:动态扩展时无法申请足够内存
     * 
     * 3. 本地方法栈(线程私有)
     * - 为Native方法服务
     * - HotSpot将虚拟机栈和本地方法栈合二为一
     * 
     * 4. Java堆(线程共享)
     * - 存放对象实例
     * - 垃圾回收的主要区域
     * - 分代:新生代(Eden、Survivor0、Survivor1)、老年代
     * - OutOfMemoryError:没有内存完成实例分配
     * 
     * 5. 方法区(线程共享)
     * - 存储类信息、常量、静态变量、即时编译后的代码
     * - 永久代(JDK7) / 元空间(JDK8)
     * - 运行时常量池:Class文件中的常量池
     * - OutOfMemoryError:无法满足内存分配需求
     */
    
    // 内存分配示例
    public void memoryAllocationDemo() {
        // 对象在Eden区分配
        byte[] allocation1 = new byte[2 * 1024 * 1024]; // 2MB
        
        // 大对象直接进入老年代
        // -XX:PretenureSizeThreshold=3M
        byte[] allocation2 = new byte[4 * 1024 * 1024]; // 4MB
    }
}

2. 垃圾回收深入理解

java

public class GarbageCollectionDeepDive {
    /*
     * 1. 判断对象是否可回收
     * - 引用计数法(无法解决循环引用)
     * - 可达性分析(GC Roots)
     *   GC Roots包括:
     *   - 虚拟机栈引用的对象
     *   - 方法区静态属性引用的对象
     *   - 方法区常量引用的对象
     *   - 本地方法栈JNI引用的对象
     */
    
    // 循环引用示例
    public void circularReferenceDemo() {
        class Node {
            Node next;
        }
        Node a = new Node();
        Node b = new Node();
        a.next = b;
        b.next = a;
        a = null;
        b = null;
        // 即使循环引用,GC Roots不可达,仍会被回收
    }
    
    /*
     * 2. 垃圾收集算法
     * 
     * 标记-清除算法
     * 优点:实现简单
     * 缺点:产生内存碎片、效率不高
     * 
     * 复制算法(新生代)
     * 原理:将内存分为两块,只使用一块,回收时将存活对象复制到另一块
     * 优点:不会产生碎片,实现简单
     * 缺点:内存利用率只有50%
     * 
     * 标记-整理算法(老年代)
     * 原理:标记后,将所有存活对象向一端移动
     * 优点:没有内存碎片
     * 缺点:移动对象需要Stop The World
     * 
     * 分代收集算法
     * 新生代:复制算法(对象存活率低)
     * 老年代:标记-清除或标记-整理(对象存活率高)
     */
    
    /*
     * 3. 垃圾收集器详解
     * 
     * Serial收集器
     * - 单线程,Client模式默认
     * - 进行垃圾回收时,必须暂停所有工作线程
     * 
     * ParNew收集器
     * - Serial的多线程版本,Server模式首选
     * - 只有它能与CMS配合工作
     * 
     * Parallel Scavenge收集器
     * - 目标是达到可控制的吞吐量
     * - 自适应调节策略
     * 
     * Serial Old收集器
     * - Serial的老年代版本,标记-整理算法
     * 
     * Parallel Old收集器
     * - Parallel Scavenge的老年代版本
     * - JDK1.6开始提供
     * 
     * CMS收集器
     * - 以获取最短回收停顿时间为目标
     * - 流程:初始标记(STW) → 并发标记 → 重新标记(STW) → 并发清除
     * - 缺点:CPU敏感、浮动垃圾、空间碎片
     * 
     * G1收集器
     * - 面向服务端,JDK9+默认
     * - 将堆划分为多个Region
     * - 可预测的停顿时间模型
     */
}

3. 类加载机制详解

java

public class ClassLoaderDeepDive {
    /*
     * 1. 类加载全过程
     * 
     * 加载:
     * - 通过类的全限定名获取二进制字节流
     * - 将字节流静态存储结构转换为方法区运行时数据结构
     * - 生成Class对象作为方法区这个类的访问入口
     * 
     * 验证:
     * - 文件格式验证(魔数、版本号等)
     * - 元数据验证(是否有父类、是否继承final类等)
     * - 字节码验证(数据流和控制流分析)
     * - 符号引用验证(匹配性检查)
     * 
     * 准备:
     * - 为类变量分配内存并设置初始值(如static int a = 123;准备阶段a=0)
     * - final static变量直接赋值为设定值
     * 
     * 解析:
     * - 将常量池内的符号引用替换为直接引用
     * - 包括类或接口解析、字段解析、方法解析等
     * 
     * 初始化:
     * - 执行类构造器<clinit>()方法
     * - 按顺序收集所有类变量的赋值动作和静态语句块
     */
    
    // 类初始化时机
    /*
     * 1. 遇到new、getstatic、putstatic、invokestatic指令时
     * 2. 使用java.lang.reflect进行反射调用时
     * 3. 初始化子类时,父类未初始化先初始化父类
     * 4. 虚拟机启动时,包含main方法的类
     * 5. 使用JDK7动态语言支持时
     */
    
    static class Parent {
        static {
            System.out.println("Parent init");
        }
    }
    
    static class Child extends Parent {
        static {
            System.out.println("Child init");
        }
    }
    
    public static void main(String[] args) {
        // 不会触发初始化的情况
        Parent[] parents = new Parent[10]; // 数组定义
        System.out.println(Child.class);   // 访问类常量
    }
    
    /*
     * 2. 类加载器
     * 
     * 启动类加载器(Bootstrap ClassLoader)
     * - 加载 JAVA_HOME/lib 下的类
     * - C++实现,Java中无法获取
     * 
     * 扩展类加载器(Extension ClassLoader)
     * - 加载 JAVA_HOME/lib/ext 下的类
     * - Java实现,sun.misc.Launcher$ExtClassLoader
     * 
     * 应用类加载器(Application ClassLoader)
     * - 加载 classpath 下的类
     * - 默认的线程上下文类加载器
     * 
     * 自定义类加载器
     * - 继承ClassLoader,重写findClass方法
     */
    
    // 双亲委派模型
    /*
     * 工作流程:
     * 1. 类加载器收到类加载请求
     * 2. 先委派给父类加载器加载
     * 3. 父类加载器无法加载时,才自己尝试加载
     * 
     * 好处:
     * - 避免类的重复加载
     * - 保证Java核心API的安全
     */
}

⚡ 三、并发编程深度解析

1. 线程基础与线程池

java

public class ThreadDeepDive {
    // 1. 线程状态转换
    /*
     * NEW → RUNNABLE → BLOCKED/WAITING/TIMED_WAITING → TERMINATED
     * 
     * NEW:新建未启动
     * RUNNABLE:可运行状态(包含就绪和运行)
     * BLOCKED:阻塞等待锁
     * WAITING:无限期等待(wait、join、park)
     * TIMED_WAITING:限期等待
     * TERMINATED:终止
     */
    
    // 2. 线程池参数详解
    public void threadPoolParams() {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            5,  // corePoolSize:核心线程数
            10, // maximumPoolSize:最大线程数
            60, // keepAliveTime:空闲线程存活时间
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100), // 工作队列
            Executors.defaultThreadFactory(), // 线程工厂
            new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
        );
        
        /*
         * 线程池工作原理:
         * 1. 提交任务,如果当前线程数 < corePoolSize,创建新线程执行
         * 2. 如果 >= corePoolSize,任务放入队列
         * 3. 如果队列已满,且线程数 < maximumPoolSize,创建新线程
         * 4. 如果线程数达到maximumPoolSize,执行拒绝策略
         * 
         * 拒绝策略:
         * AbortPolicy:抛出异常(默认)
         * CallerRunsPolicy:调用者线程执行
         * DiscardPolicy:丢弃任务
         * DiscardOldestPolicy:丢弃队列最老的任务
         */
    }
    
    // 3. 四种线程池的隐患
    /*
     * FixedThreadPool
     * - 核心线程数=最大线程数
     * - 使用LinkedBlockingQueue(无界队列)
     * - 隐患:队列可能无限增长,导致OOM
     * 
     * CachedThreadPool
     * - 核心线程0,最大线程Integer.MAX_VALUE
     * - 使用SynchronousQueue
     * - 隐患:线程数无限增长,导致OOM
     * 
     * SingleThreadExecutor
     * - 核心线程=最大线程=1
     * - 使用LinkedBlockingQueue
     * - 隐患:队列可能无限增长
     * 
     * ScheduledThreadPool
     * - 使用DelayedWorkQueue
     * - 隐患:队列可能无限增长
     */
}

2. 锁的深入理解

java

public class LockDeepDive {
    // 1. synchronized 原理
    /*
     * 底层实现:
     * - 代码块:monitorenter + monitorexit
     * - 方法:ACC_SYNCHRONIZED标志
     * 
     * 锁升级过程(无锁 → 偏向锁 → 轻量级锁 → 重量级锁)
     * 
     * 偏向锁:
     * - 锁对象头记录线程ID
     * - 同一线程再次获取锁,只需比较线程ID
     * 
     * 轻量级锁:
     * - CAS获取锁
     * - 适用于线程交替执行
     * 
     * 重量级锁:
     * - 依赖操作系统的Mutex Lock
     * - 线程阻塞和唤醒需要上下文切换
     */
    
    // 2. ReentrantLock 原理
    /*
     * 基于AQS(AbstractQueuedSynchronizer)
     * 
     * AQS原理:
     * - 维护volatile int state(同步状态)
     * - CLH队列(双向链表)
     * 
     * 独占模式:
     * tryAcquire → tryRelease
     * 
     * 共享模式:
     * tryAcquireShared → tryReleaseShared
     */
    
    // 3. 死锁示例和排查
    public void deadLockDemo() {
        Object lock1 = new Object();
        Object lock2 = new Object();
        
        Thread t1 = new Thread(() -> {
            synchronized (lock1) {
                try { Thread.sleep(100); } catch (Exception e) {}
                synchronized (lock2) {
                    System.out.println("t1完成");
                }
            }
        });
        
        Thread t2 = new Thread(() -> {
            synchronized (lock2) {
                try { Thread.sleep(100); } catch (Exception e) {}
                synchronized (lock1) {
                    System.out.println("t2完成");
                }
            }
        });
        
        t1.start();
        t2.start();
        
        // 排查方法:
        // 1. jps 查看java进程
        // 2. jstack <pid> 查看线程堆栈
        // 3. jconsole/jvisualvm 图形化工具
    }
    
    // 4. volatile 和 happen-before
    /*
     * volatile特性:
     * 1. 可见性:写操作立即刷新到主存,读操作从主存读取
     * 2. 禁止指令重排序
     * 
     * happen-before规则:
     * 1. 程序次序规则
     * 2. volatile变量规则
     * 3. 锁规则
     * 4. 传递性
     * 5. 线程启动规则
     * 6. 线程终止规则
     * 7. 线程中断规则
     * 8. 对象终结规则
     */
}

🏗️ 四、Spring框架源码级理解

1. IOC容器源码分析

java

public class SpringIOCDeepDive {
    /*
     * 1. BeanFactory 和 ApplicationContext 区别
     * BeanFactory:延迟加载,第一次getBean才创建
     * ApplicationContext:预加载,容器启动时创建
     * 
     * 2. BeanFactory 继承体系
     * BeanFactory
     *   └── HierarchicalBeanFactory
     *        └── ConfigurableBeanFactory
     *             └── AutowireCapableBeanFactory
     *                  └── ListableBeanFactory
     *                       └── ApplicationContext
     */
    
    // 3. BeanDefinition 解析过程
    /*
     * XML配置方式:
     * XmlBeanDefinitionReader
     * 1. 加载XML文件(ResourceLoader)
     * 2. 解析XML(DocumentLoader)
     * 3. 注册BeanDefinition(BeanDefinitionRegistry)
     * 
     * 注解配置方式:
     * ClassPathBeanDefinitionScanner
     * 1. 扫描指定包下的类
     * 2. 过滤出带有@Component等注解的类
     * 3. 生成BeanDefinition并注册
     */
    
    // 4. Bean生命周期源码级别
    @Component
    public class BeanLifecycleDemo implements BeanNameAware, BeanFactoryAware,
            InitializingBean, DisposableBean {
        
        private String name;
        
        public BeanLifecycleDemo() {
            System.out.println("1. 实例化");
        }
        
        @Autowired
        public void setName(String name) {
            System.out.println("2. 属性赋值");
            this.name = name;
        }
        
        @Override
        public void setBeanName(String name) {
            System.out.println("3. BeanNameAware");
        }
        
        @Override
        public void setBeanFactory(BeanFactory beanFactory) {
            System.out.println("4. BeanFactoryAware");
        }
        
        @PostConstruct
        public void postConstruct() {
            System.out.println("5. @PostConstruct");
        }
        
        @Override
        public void afterPropertiesSet() {
            System.out.println("6. InitializingBean");
        }
        
        @Bean(initMethod = "initMethod")
        public void initMethod() {
            System.out.println("7. init-method");
        }
        
        @PreDestroy
        public void preDestroy() {
            System.out.println("8. @PreDestroy");
        }
        
        @Override
        public void destroy() {
            System.out.println("9. DisposableBean");
        }
        
        // 完整流程:
        // 实例化 → 属性赋值 → Aware接口 → BeanPostProcessor前置处理
        // → @PostConstruct → InitializingBean → init-method
        // → BeanPostProcessor后置处理 → 使用中 → @PreDestroy
        // → DisposableBean → destroy-method
    }
}

2. AOP原理深度解析

java

public class AOPDeepDive {
    /*
     * 1. AOP概念
     * Aspect:切面(@Aspect)
     * JoinPoint:连接点(方法调用)
     * Advice:通知(@Before、@After等)
     * Pointcut:切点(匹配连接点的表达式)
     * Introduction:引入(为类添加新方法属性)
     * Target Object:目标对象
     * AOP Proxy:代理对象
     * Weaving:织入
     */
    
    // 2. 动态代理两种方式
    /*
     * JDK动态代理
     * - 基于接口,实现InvocationHandler
     * - 生成代理类实现目标接口
     * - 适用于有接口的情况
     */
    public class JdkProxyDemo {
        public Object createProxy(Object target) {
            return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    System.out.println("前置增强");
                    Object result = method.invoke(target, args);
                    System.out.println("后置增强");
                    return result;
                }
            );
        }
    }
    
    /*
     * CGLIB动态代理
     * - 基于继承,实现MethodInterceptor
     * - 生成目标类的子类
     * - 适用于没有接口的类
     */
    public class CglibProxyDemo implements MethodInterceptor {
        public Object createProxy(Class<?> targetClass) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(targetClass);
            enhancer.setCallback(this);
            return enhancer.create();
        }
        
        @Override
        public Object intercept(Object obj, Method method, 
                Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("前置增强");
            Object result = proxy.invokeSuper(obj, args);
            System.out.println("后置增强");
            return result;
        }
    }
    
    // 3. Spring AOP 选择策略
    /*
     * 如果目标类实现了接口,默认使用JDK动态代理
     * 如果没有实现接口,使用CGLIB
     * 可以通过配置强制使用CGLIB
     * @EnableAspectJAutoProxy(proxyTargetClass = true)
     */
}

3. Spring事务管理

java

@Service
public class TransactionDeepDive {
    /*
     * 1. 事务传播行为
     * 
     * REQUIRED(默认)
     * - 支持当前事务,如果没有则创建新事务
     * 
     * SUPPORTS
     * - 支持当前事务,如果没有则以非事务方式执行
     * 
     * MANDATORY
     * - 支持当前事务,如果没有则抛出异常
     * 
     * REQUIRES_NEW
     * - 创建新事务,如果有当前事务则挂起
     * 
     * NOT_SUPPORTED
     * - 以非事务方式执行,如果有当前事务则挂起
     * 
     * NEVER
     * - 以非事务方式执行,如果有当前事务则抛出异常
     * 
     * NESTED
     * - 如果当前事务存在,则嵌套事务执行
     */
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        // 事务方法
    }
    
    // 2. 事务失效的场景
    /*
     * 1. 数据库引擎不支持事务(MyISAM)
     * 2. 注解在非public方法上
     * 3. 同一个类中方法调用(自调用)
     * 4. 异常被catch捕获未抛出
     * 5. 抛出检查异常(非RuntimeException)
     * 6. 传播行为设置问题
     */
    
    @Autowired
    private TransactionDeepDive self;
    
    public void callMethod() {
        // 错误:事务失效
        methodA(); // 自调用
        
        // 正确:通过代理调用
        self.methodA();
    }
    
    // 3. 事务隔离级别
    /*
     * DEFAULT:使用数据库默认
     * READ_UNCOMMITTED:读未提交
     * READ_COMMITTED:读已提交
     * REPEATABLE_READ:可重复读
     * SERIALIZABLE:串行化
     */
}

🗃️ 五、数据库深度解析

1. MySQL索引原理

sql

-- 1. B+树索引详解
/*
 * 为什么用B+树:
 * - 非叶子节点不存储数据,可以存储更多索引
 * - 所有数据都在叶子节点,查询稳定
 * - 叶子节点双向链表,范围查询高效
 * 
 * 聚簇索引(InnoDB)
 * - 数据和索引存储在一起
 * - 主键索引就是聚簇索引
 * - 叶子节点存储完整行数据
 * 
 * 非聚簇索引(MyISAM)
 * - 数据和索引分开存储
 * - 叶子节点存储数据地址
 */

-- 2. 索引使用原则
CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `age` int DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name_age` (`name`,`age`), -- 复合索引
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB;

-- 最左前缀原则
EXPLAIN SELECT * FROM user WHERE name = '张三'; -- 使用索引
EXPLAIN SELECT * FROM user WHERE age = 20; -- 不使用索引
EXPLAIN SELECT * FROM user WHERE name = '张三' AND age = 20; -- 使用索引

-- 索引下推(ICP)
SELECT * FROM user WHERE name LIKE '张%' AND age = 20;
-- 5.6之前:根据name找到主键,回表查数据,再过滤age
-- 5.6之后:根据name找到主键时,直接在引擎层过滤age

-- 3. 索引失效场景
-- 对索引列使用函数
EXPLAIN SELECT * FROM user WHERE YEAR(create_time) = 2023;

-- 隐式类型转换
EXPLAIN SELECT * FROM user WHERE name = 123; -- name是varchar类型

-- 不符合最左前缀
EXPLAIN SELECT * FROM user WHERE age = 20;

-- like以%开头
EXPLAIN SELECT * FROM user WHERE name LIKE '%三';

-- or条件
EXPLAIN SELECT * FROM user WHERE name = '张三' OR age = 20;

2. SQL优化实战

sql

-- 1. 分页优化
-- 传统分页(越往后越慢)
SELECT * FROM user ORDER BY id LIMIT 100000, 20;

-- 优化方案1:记录上次位置
SELECT * FROM user WHERE id > 100000 ORDER BY id LIMIT 20;

-- 优化方案2:子查询
SELECT * FROM user 
WHERE id >= (SELECT id FROM user ORDER BY id LIMIT 100000, 1) 
LIMIT 20;

-- 2. 连接优化
-- 小表驱动大表
-- 假设user表100条,order表1万条
-- 推荐
SELECT * FROM user u INNER JOIN order o ON u.id = o.user_id;

-- 3. 排序优化
-- 避免filesort
CREATE INDEX idx_age_name ON user(age, name);

-- 排序字段和查询字段一致
SELECT age, name FROM user ORDER BY age, name;

-- 4. 分组优化
-- 使用索引
CREATE INDEX idx_user_date ON order(user_id, create_time);

SELECT user_id, COUNT(*) 
FROM order 
WHERE create_time >= '2024-01-01' 
GROUP BY user_id;

3. MySQL锁机制

sql

-- 1. 行锁、表锁、间隙锁
-- 表锁
LOCK TABLES user READ;
LOCK TABLES user WRITE;

-- 行锁(InnoDB)
-- 共享锁(S锁)
SELECT * FROM user WHERE id = 1 LOCK IN SHARE MODE;

-- 排他锁(X锁)
SELECT * FROM user WHERE id = 1 FOR UPDATE;

-- 间隙锁(解决幻读)
-- 锁住的是索引记录之间的间隙
SELECT * FROM user WHERE age BETWEEN 10 AND 20 FOR UPDATE;

-- 2. 死锁案例
-- 事务1
BEGIN;
UPDATE user SET name = '张三' WHERE id = 1;
UPDATE user SET name = '李四' WHERE id = 2;
COMMIT;

-- 事务2
BEGIN;
UPDATE user SET name = '王五' WHERE id = 2;
UPDATE user SET name = '赵六' WHERE id = 1;
COMMIT;

-- 3. 死锁排查
-- 查看当前事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;

-- 查看锁等待
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

-- 查看锁等待事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

-- 开启死锁日志
SET GLOBAL innodb_print_all_deadlocks = ON;

🔧 六、Redis深度解析

1. Redis数据结构底层实现

java

public class RedisDataStructure {
    /*
     * 1. String(SDS - Simple Dynamic String)
     * - 预分配空间,减少内存重分配次数
     * - 二进制安全
     * - 兼容C字符串
     * 
     * 2. List(quicklist)
     * - 压缩列表(ziplist)+ 双向链表
     * - 元素少时用ziplist节省内存
     * - 元素多时转为双向链表
     * 
     * 3. Hash
     * - 元素少时用ziplist
     * - 元素多时用dict(哈希表)
     * - 渐进式rehash
     * 
     * 4. Set
     * - 元素都是整数且少时用intset
     * - 否则用dict
     * 
     * 5. ZSet(跳表)
     * - 元素少时用ziplist
     * - 否则用skiplist + dict
     * - 跳表:多级索引实现O(logN)查询
     */
    
    // 2. 跳表实现原理
    /*
     * 为什么用跳表不用平衡树?
     * - 实现简单
     * - 范围查询方便
     * - 更容易控制索引层级
     * 
     * 层级:随机生成,1级概率50%,2级25%,以此类推
     * 查询:从最高层开始,找到最后一个小于目标值的节点,然后降层
     */
}

2. 缓存问题解决方案

java

@Service
public class RedisCacheSolution {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 1. 缓存穿透(查询不存在的数据)
    public String getDataWithPenetration(String key) {
        // 从缓存获取
        String value = redisTemplate.opsForValue().get(key);
        if (value != null) {
            return value;
        }
        
        // 缓存不存在,从数据库查询
        value = queryFromDB(key);
        
        if (value != null) {
            // 数据库存在,写入缓存
            redisTemplate.opsForValue().set(key, value, 1, TimeUnit.HOURS);
        } else {
            // 数据库不存在,缓存空值(设置较短过期时间)
            redisTemplate.opsForValue().set(key, "NULL", 5, TimeUnit.MINUTES);
        }
        
        return value;
    }
    
    // 2. 缓存击穿(热点key过期)
    // 方案