以下是 Java 面试高频 10 道题 + 精炼答案(覆盖基础核心、JVM、并发、集合等重点,适合面试快速记忆):
1. 谈谈 Java 的三大特性(封装、继承、多态)
- 封装:隐藏对象内部实现细节,通过
private 限制访问,提供 getter/setter 等公共方法交互,降低耦合、提高安全性(比如实体类的属性封装)。 - 继承:子类通过
extends 继承父类非私有属性和方法,实现代码复用;但 Java 只支持单继承(避免菱形依赖),可通过接口间接实现多继承。 - 多态:同一行为的不同表现形式,核心是「方法重写(子类覆写父类方法)」和「向上转型(父类引用指向子类对象)」,比如
List list = new ArrayList<>(),调用 list.add() 时实际执行 ArrayList 的实现,提高代码灵活性。
2. String、StringBuffer、StringBuilder 的区别
| 特性 | String | StringBuffer | StringBuilder |
|---|---|---|---|
| 可变性 | 不可变(final 修饰字符数组) | 可变(数组扩容) | 可变(数组扩容) |
| 线程安全 | 安全(无修改操作) | 安全(方法加 synchronized) | 不安全(无锁) |
| 效率 | 最低(修改会创建新对象) | 中等 | 最高 |
| 适用场景 | 少量字符串操作 | 多线程环境字符串拼接 | 单线程环境字符串拼接 |
3. 什么是 JVM 内存模型?主要分区有哪些?
JVM 内存模型(JMM)定义了线程如何通过内存交互,解决多线程可见性、原子性、有序性问题; 核心分区(基于 HotSpot 虚拟机):
- 程序计数器:线程私有,记录当前线程执行的字节码行号,无 OOM 可能。
- 虚拟机栈:线程私有,存储方法调用的栈帧(局部变量、操作数栈等),栈深度溢出会抛
StackOverflowError,扩容失败抛 OutOfMemoryError。 - 本地方法栈:线程私有,为 Native 方法(如
System.currentTimeMillis())提供内存支持,同样可能抛 StackOverflowError/OOM。 - 堆:线程共享,存储对象实例和数组,是 GC 主要区域(分年轻代、老年代),OOM 高频发生区。
- 方法区:线程共享,存储类信息、常量、静态变量等(JDK 8 后用元空间 Metaspace 实现,占用本地内存,默认无上限)。
4. 什么是 GC(垃圾回收)?常见 GC 算法和收集器有哪些?
- GC 定义:自动回收堆中“不可达对象”(无引用指向的对象)的内存,避免内存泄漏,无需手动释放。
- 核心算法:
- 标记-清除:先标记垃圾,再清除,效率低、会产生内存碎片。
- 复制算法:将内存分为两块,存活对象复制到另一块,清除原块,无碎片、效率高(年轻代默认用)。
- 标记-整理:标记后将存活对象向一端移动,再清除剩余区域(老年代默认用)。
- 常见收集器:
- 年轻代:SerialGC(串行,单线程)、Parallel Scavenge(并行,注重吞吐量)。
- 老年代:Serial Old(串行)、Parallel Old(并行)、CMS(并发,注重响应时间)、G1(区域分代,兼顾吞吐量和响应时间)。
5. Java 中实现多线程的三种方式及区别
- 方式 1:继承 Thread 类:重写
run() 方法,调用 start() 启动线程(底层调用 start0() native 方法)。 缺点:单继承限制,无法继承其他类。 - 方式 2:实现 Runnable 接口:实现
run() 方法,通过 new Thread(runnable).start() 启动。 优点:无继承限制,可实现多个接口;缺点:无法直接获取返回值。 - 方式 3:实现 Callable 接口:实现
call() 方法(可返回值、抛异常),结合 FutureTask 包装,通过线程池启动。 优点:支持返回值和异常处理,适合异步任务;缺点:代码稍复杂。
6. 什么是线程安全?如何保证线程安全?
- 线程安全:多线程并发访问共享资源时,不会出现数据错乱、死锁等问题(比如多线程抢票,不会出现超卖、重复售票)。
- 实现方式:
- 锁机制:
synchronized(关键字,可修饰方法/代码块,底层是监视器锁)、ReentrantLock(类锁,支持公平锁/非公平锁、可中断)。 - 无锁机制:
volatile(修饰变量,保证可见性和有序性,不保证原子性)、原子类(AtomicInteger,基于 CAS 实现原子操作)。 - 线程封闭:局部变量(线程私有,无共享)、ThreadLocal(每个线程独立存储副本,避免共享)。
- 并发容器:
ConcurrentHashMap(分段锁/CAS 实现线程安全)、CopyOnWriteArrayList(写时复制,读无锁)。
7. HashMap 和 ConcurrentHashMap 的区别(JDK 8+)
| 特性 | HashMap | ConcurrentHashMap |
|---|---|---|
| 线程安全 | 不安全(多线程put可能扩容死循环、数据丢失) | 安全 |
| 实现方式 | 数组+链表/红黑树(链表长度>8转红黑树) | 数组+链表/红黑树+CAS+synchronized(分段锁优化为节点锁) |
| 支持操作 | 无原子操作,需手动加锁 | 支持 putIfAbsent 等原子操作 |
| 遍历特性 | 快速失败(modCount校验,并发修改抛 ConcurrentModificationException) | 弱一致性(遍历不抛异常,可能读取旧数据) |
| 适用场景 | 单线程环境 | 多线程并发环境 |
8. 什么是异常体系?Checked Exception 和 Unchecked Exception 的区别
- 异常体系:顶层是
Throwable,分为 Error(错误,如 OutOfMemoryError,JVM 层面,无法捕获修复)和 Exception(异常,可处理)。 - Checked Exception(受检异常) :编译期强制检查,必须捕获或声明抛出(如
IOException、SQLException),避免遗漏处理。 - Unchecked Exception(非受检异常) :编译期不检查,继承自
RuntimeException(如 NullPointerException、ArrayIndexOutOfBoundsException),通常是代码逻辑错误,无需强制捕获,建议通过代码优化避免。
9. 谈谈你对 Spring IoC 和 AOP 的理解
- IoC(控制反转) :
- 核心:将对象的创建、依赖注入交给 Spring 容器管理,而非手动
new 对象,降低组件耦合。 - 实现:通过 XML 配置、注解(
@Component、@Autowired)或 Java 配置类定义 Bean,容器启动时初始化 Bean 并注入依赖。 - 好处:解耦、便于测试(可替换依赖的模拟对象)、提高代码复用。
- AOP(面向切面编程) :
- 核心:在不修改原有代码的前提下,对方法进行增强(如日志、事务、权限校验),将通用逻辑抽离为“切面”。
- 关键概念:切点(Pointcut,指定增强的方法)、通知(Advice,增强的逻辑,如
@Before、@AfterReturning)、切面(Aspect,切点+通知)、动态代理(JDK 动态代理基于接口,CGLIB 基于子类,Spring 自动选择)。 - 应用:Spring 事务管理(
@Transactional)、全局日志打印、接口权限拦截。
10. 什么是分布式事务?常见解决方案有哪些?
- 分布式事务:跨多个服务/数据库的事务(如下单流程:扣库存、减余额、创建订单分属不同服务),需保证“所有操作成功或所有失败”,满足 ACID 特性。
- 常见解决方案:
- 2PC(两阶段提交):分为准备阶段(所有参与者确认可提交)和提交阶段(协调者通知提交),缺点是阻塞、协调者单点故障。
- TCC(Try-Confirm-Cancel):业务层实现 Try(资源检查预留)、Confirm(确认提交)、Cancel(回滚释放),无锁、高性能,缺点是代码侵入性强。
- 本地消息表+MQ:本地事务和消息表原子提交,通过 MQ 异步通知其他服务,失败则重试,适合最终一致性场景(如订单通知)。
- Saga 模式:将分布式事务拆分为多个本地事务,每个事务失败时执行补偿操作,适合长事务(如物流履约)。
- 事务消息(RocketMQ 支持):MQ 保证消息可靠投递,接收方确认后才提交本地事务,简化一致性实现。
这些题目覆盖了 Java 基础、JVM、并发、集合、Spring 框架、分布式核心考点,答案简洁且直击要点,面试时可结合实际项目经验补充案例(比如“项目中用 ConcurrentHashMap 解决了多线程统计问题”),效果更好。