Java
目录
核心学习路径 (路线图)
作为一个合格的 Java 开发,或者想要转/了解后端的同学,可以按照以下路线进行体系化学习:
- Java SE 基础
- 面向对象三大特性(封装、继承、多态)
- 基本数据类型、包装类、自动装箱/拆箱
- 异常处理机制(Exception vs Error)
- 反射与注解(AOP 的基础)
- 集合框架 (Collections)
List:ArrayList,LinkedList的底层结构与适用场景Set:HashSet,TreeSet及其去重原理Map:HashMap(JDK 1.7 vs 1.8),ConcurrentHashMap的线程安全实现
- 并发编程 (JUC)
- 线程与进程、线程的生命周期
- 线程池(
ThreadPoolExecutor的 7 大核心参数及工作原理) - 锁机制:
synchronized的锁升级过程、ReentrantLock(AQS 原理)、CAS 乐观锁 ThreadLocal内存泄漏问题及应用场景
- JVM (Java 虚拟机)
- 内存模型(堆、栈、方法区/元空间、程序计数器)
- 垃圾回收机制(GC 算法:标记清除、复制、标记整理;GC 收集器:CMS, G1)
- 类加载机制(双亲委派模型及打破双亲委派)
- 主流框架生态
- Spring: IoC(控制反转)与 AOP(面向切面编程)的实现原理
- Spring Boot: 自动装配原理、Starter 机制
- MyBatis: ORM 映射、缓存机制(一级/二级缓存)、动态 SQL
常见高频面试题精选
基础篇
1. String、StringBuffer 和 StringBuilder 的区别?
String:不可变类(底层由final byte[]或char[]修饰),每次修改都会产生新的对象,适合少量字符串操作。StringBuffer:可变类,线程安全(方法上加了synchronized锁),性能较差,适合多线程环境下的字符串拼接。StringBuilder:可变类,线程不安全,性能最高,适合单线程下大量字符串拼接。
2. == 和 equals() 的区别?
==:对于基本数据类型,比较的是值;对于引用类型,比较的是内存地址。equals():默认情况下(继承自Object)与==一样比较地址;但大多数类(如String、Integer)都重写了该方法,用于比较对象的内容是否相等。
3. 接口 (Interface) 和 抽象类 (Abstract Class) 的区别?
- 设计理念:接口是对“行为”的抽象(像是一个契约);抽象类是对“本质”的抽象(是一种模板)。
- 实现机制:一个类可以实现多个接口(
implements),但只能继承一个抽象类(extends)。 - 成员变量:接口中的变量默认都是
public static final的常量;抽象类中可以有普通成员变量。
集合篇
4. HashMap 的底层实现原理是什么?
- JDK 1.7:数组 + 链表。使用头插法,在多线程并发扩容时容易产生死循环(环形链表)。
- JDK 1.8:数组 + 链表 + 红黑树。当链表长度大于 8 且数组长度大于 64 时,链表会转化为红黑树,将查询时间复杂度从 O(n) 降低到 O(log n);同时改用尾插法解决了死循环问题。
5. ConcurrentHashMap 是如何保证线程安全的?
- JDK 1.7:采用 分段锁 (Segment) 机制,将整个 Map 分成多个 Segment,每次只锁住一个 Segment,默认支持 16 的并发度。
- JDK 1.8:抛弃了 Segment,改用 Node 数组 + 链表 + 红黑树。采用
CAS + synchronized锁住数组的头节点(粒度更细,锁住了桶的头部),大大提升了并发性能。
并发篇
6. 线程池的创建方式及核心参数?
不推荐使用 Executors 创建线程池(因为容易导致 OOM),推荐直接使用 ThreadPoolExecutor。 它的 7 大核心参数为:
corePoolSize:核心线程数。maximumPoolSize:最大线程数。keepAliveTime:空闲线程的存活时间。unit:存活时间的单位。workQueue:任务阻塞队列(如ArrayBlockingQueue、LinkedBlockingQueue)。threadFactory:线程工厂,用于定制线程(如设置名称)。handler:拒绝策略(如AbortPolicy直接抛异常,CallerRunsPolicy交给调用者执行等)。
工作流程:任务来了先看核心线程是否已满 -> 未满则创建 -> 满了则放入队列 -> 队列满了则看最大线程数是否已满 -> 未满则创建非核心线程 -> 满了则执行拒绝策略。
7. 什么是 ThreadLocal?存在什么问题?
- 概念:提供线程局部变量,让每个线程都有自己专属的变量副本,解决多线程下变量并发访问的安全问题(空间换时间)。
- 内存泄漏问题:
ThreadLocalMap中的 Key 是弱引用,Value 是强引用。当垃圾回收时 Key 会被回收,但 Value 还在,导致null: Value的脏数据。 - 解决:每次用完
ThreadLocal后,务必手动调用remove()方法清理。
JVM 与框架篇
8. JVM 内存区域是怎么划分的?
- 线程私有:
- 程序计数器:记录当前线程执行的字节码行号。
- 虚拟机栈:存储局部变量表、操作数栈等,也就是常说的“栈内存”。
- 本地方法栈:为执行 Native 方法服务。
- 线程共享:
- 堆 (Heap):存放对象实例,是垃圾回收(GC)的主要区域。
- 方法区 (元空间 MetaSpace):存放已被加载的类信息、常量、静态变量等。
9. Spring 的 IoC 和 AOP 是什么?
- IoC (控制反转):将对象的创建和依赖注入的权力交给了 Spring 容器管理,降低了代码的耦合度。核心是 DI(依赖注入)。
- AOP (面向切面编程):在不修改原代码的情况下,对现有方法进行增强(如统一日志打印、事务管理、权限校验)。底层原理是动态代理(JDK 动态代理或 CGLIB 代理)。
10. Spring Boot 自动装配原理?
核心注解 @SpringBootApplication 包含三个关键注解:
@SpringBootConfiguration:标识这是一个配置类。@ComponentScan:自动扫描并加载符合条件的组件(如@Service、@Controller)。@EnableAutoConfiguration:核心!它通过@Import导入AutoConfigurationImportSelector,去读取META-INF/spring.factories(或META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports)文件中的配置类,并根据@Conditional条件注解判断是否需要将其加载到 Spring 容器中,从而实现开箱即用。
