一、Java核心基础深度剖析

集合框架与底层数据结构

在Java中级面试中,集合框架不仅是基础,更是考察候选人对内存管理、数据结构理解程度的试金石。面试官不再满足于“HashMap和HashTable的区别”,而是会深入询问HashMap在JDK1.8后的红黑树转换机制、扩容逻辑以及并发下的安全隐患。

1.1 HashMap源码级解析

HashMap是Java中最常用的数据结构之一。理解其底层实现是Java中级面试重点中的重中之重。

⚡ 底层结构

JDK1.7采用数组+链表,JDK1.8引入数组+链表+红黑树。当链表长度超过8且数组长度超过64时,链表转换为红黑树,将查询时间复杂度从O(n)优化至O(log n)。

⚡ 哈希冲突解决

采用链地址法。通过hashCode计算数组索引,若冲突则通过next指针形成链表或树结构。JDK1.8优化了hash算法,减少高位参与运算,降低冲突概率。

⚡ 扩容机制

默认容量16,负载因子0.75。扩容时容量翻倍,并重新计算元素位置。JDK1.8采用位运算优化,元素要么在原位置,要么在原位置+旧容量处,无需重新hash。

1.2 ConcurrentHashMap实现原理

相比Hashtable和Collections.synchronizedMap,ConcurrentHashMap提供了更高的并发度。JDK1.7使用分段锁(Segment),JDK1.8则采用CAS + synchronized保证线程安全,锁粒度更细,性能更优。

// 示例:简单的线程安全计数器
// 中级面试常问:为什么不用AtomicInteger?
// 答:AtomicInteger适合单字段原子操作,若涉及复合操作(如check-then-act),
// 仍需使用ConcurrentHashMap或显式锁。
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
    private AtomicInteger count = new AtomicInteger(0);
    public void increment() {
        count.incrementAndGet();
    }
}

二、主流框架源码与扩展

Spring IoC/AOP与事务管理

掌握Spring框架不仅是会用注解,更要理解其设计模式和生命周期。Spring的核心在于IoC容器管理和AOP切面编程。

Bean生命周期
事务传播机制
AOP原理

Spring Bean的生命周期

这是面试中的高频考点,流程复杂且细节众多。

  • ① 实例化:通过构造器或工厂方法创建Bean实例。
  • ② 属性赋值:填充Bean的所有属性,执行set方法。
  • ③ 初始化:
    • 检查Aware接口(如BeanNameAware)。
    • 执行BeanPostProcessor的postProcessBeforeInitialization。
    • 执行InitializingBean的afterPropertiesSet或自定义init-method。
    • 执行BeanPostProcessor的postProcessAfterInitialization(AOP代理在此阶段生成)。
  • ④ 销毁:执行DisposableBean或自定义destroy-method。

事务传播行为详解

传播行为 说明 场景示例
REQUIRED 默认值。如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新事务。 大多数业务操作,如用户注册。
REQUIRES_NEW 创建一个新事务,如果当前存在事务,则把当前事务挂起。 日志记录,无论主业务是否成功,日志必须保存。
NESTED 如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与REQUIRED类似的操作。 复杂业务中的子模块,支持部分回滚。
NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,则把当前事务挂起。 耗时且不需要事务的操作,如统计报表生成。

AOP代理机制

Spring AOP默认使用JDK动态代理(基于接口),如果目标对象没有实现接口,则使用CGLIB(基于子类)。

在Spring Boot 2.x之后,默认倾向于使用CGLIB,可通过spring.aop.proxy-target-class=true强制启用。

// AOP切面示例
@Aspect
@Component
public class LogAspect {
    @Around("@annotation(com.example.annotation.BusinessLog)")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        System.out.println("方法执行耗时: " + (endTime - startTime) + "ms");
        return result;
    }
}

三、Java并发编程实战

JUC包与线程池参数

并发编程是区分初级与中级工程师的关键分水岭。理解线程池参数、锁机制以及内存模型是必备技能。

3.1 线程池七大参数详解

创建线程池时,必须明确指定参数,避免使用Executors工具类(可能导致OOM)。

参数1:corePoolSize

核心线程数

线程池中保持存活的线程数量,即使它们是空闲的。除非设置allowCoreThreadTimeOut,否则不会被回收。

参数2:maximumPoolSize

最大线程数

线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则创建新线程执行任务。

参数3:keepAliveTime

存活时间

线程空闲超过此时间后,非核心线程将被回收。对于核心线程,若allowCoreThreadTimeOut=true,同样适用。

参数4:workQueue

任务队列

用于存储等待执行的任务。常用有ArrayBlockingQueue(有界)、LinkedBlockingQueue(无界,慎用)、SynchronousQueue(不存储,直接移交)。

3.2 volatile关键字与内存屏障

volatile保证了变量的可见性和有序性,但不保证原子性。它通过插入内存屏障防止指令重排。

// 双重检查锁定(DCL)单例模式
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(); // 非原子操作,需volatile防止重排
                }
            }
        }
        return instance;
    }
}

四、MySQL性能优化与索引

B+树与事务隔离级别

中级Java工程师必须深入理解数据库底层原理。索引结构、事务隔离级别以及慢查询优化是日常开发和面试的核心。

4.1 为什么使用B+树而不是B树或Hash?

  • 【B+树】:非叶子节点只存储键,不存储数据,单个节点能容纳更多索引项,降低树高,减少IO次数。所有数据存储在叶子节点,且叶子节点之间通过双向链表连接,利于范围查询。
  • 【B树】:非叶子节点也存储数据,导致节点容纳索引变少,树高增加,IO次数增多。
  • 【Hash】:仅支持等值查询,不支持范围查询和排序。

4.2 索引失效场景

❌ 模糊查询左通配符

LIKE '%abc' 会导致全表扫描,因为最左匹配原则被破坏。

❌ 类型隐式转换

字符串字段不加引号,如 WHERE name = 123,会导致索引失效。

❌ 函数计算

WHERE YEAR(create_time) = 2023,对索引列进行函数操作会导致失效。

4.3 事务隔离级别与MVCC

MySQL InnoDB引擎默认采用可重复读(Repeatable Read)。通过undo log和Read View实现MVCC(多版本并发控制),保证在RR级别下,事务内多次读取结果一致,并通过Next-Key Lock解决幻读问题。

五、JVM内存模型与调优

GC算法与参数配置

理解JVM内存结构是解决线上OOM(OutOfMemoryError)和CPU飙高的基础。

5.1 内存区域划分

  • 【线程私有】:程序计数器、虚拟机栈、本地方法栈。
  • 【线程共享】:堆(Heap)、方法区(Metaspace)。

5.2 常见GC算法对比

算法 优点 缺点
标记-清除 实现简单 产生大量内存碎片,效率较低
复制算法 无内存碎片,效率高 内存利用率仅50%(适用于新生代)
标记-整理 无内存碎片 移动对象成本高(适用于老年代)

5.3 常用JVM参数

-Xms1g -Xmx1g          # 初始堆和最大堆大小一致,避免动态扩容
-Xmn512m               # 新生代大小
-XX:+UseG1GC           # 使用G1垃圾收集器
-XX:MaxGCPauseMillis=200 # 最大GC停顿时间目标
-XX:+HeapDumpOnOutOfMemoryError # OOM时导出堆快照

六、分布式架构与中间件

Redis、MQ与分布式锁

在微服务架构下,Redis和RabbitMQ/Kafka是解决高并发、高可用的关键组件。

6.1 Redis缓存穿透、击穿、雪崩

  • 【穿透】:查询不存在的数据。对策:布隆过滤器、缓存空对象。
  • 【击穿】:热点Key过期。对策:互斥锁、逻辑过期。
  • 【雪崩】:大量Key同时过期或Redis宕机。对策:随机过期时间、集群部署、限流降级。

6.2 分布式锁实现

基于Redis的分布式锁通常使用SETNX命令,并配合EXPIRE设置超时时间,防止死锁。更推荐使用Redisson客户端,它实现了看门狗(Watchdog)机制,自动续期。

// Redisson分布式锁示例
RLock lock = redisson.getLock("myLock");
try {
    lock.lock();
    // 业务逻辑
} finally {
    lock.unlock();
}