一、并发相关对比

1. synchronized vs ReentrantLock

对比 synchronized ReentrantLock
所属层面 JVM 层面 JDK 层面
底层 Monitor 对象监视器 AQS
加锁方式 隐式加锁、自动释放 手动 lock/unlock
是否可中断 不支持 支持 lockInterruptibly
是否支持公平锁 不支持 支持公平/非公平
是否支持多个条件队列 不支持 支持多个 Condition

面试一句话:

synchronized 是 JVM 层面的内置锁,使用简单,自动释放;ReentrantLock 是 JDK 层面基于 AQS 实现的显式锁,功能更强,比如支持公平锁、可中断锁、超时获取锁和多个条件队列。


2. volatile vs synchronized

对比 volatile synchronized
解决问题 可见性、有序性 原子性、可见性、有序性
是否加锁 不加锁 加锁
是否保证复合操作原子性 不保证 保证
典型场景 状态标记、双重检查锁 临界区互斥

面试一句话:

volatile 只能保证变量修改对其他线程可见,并禁止指令重排序,但不能保证 i++ 这种复合操作的原子性;synchronized 可以保证同一时刻只有一个线程进入临界区,所以能保证原子性。


3. sleep vs wait

对比 sleep wait
所属类 Thread Object
是否释放锁 不释放锁 释放锁
使用位置 任意位置 必须在 synchronized 内
唤醒方式 时间到自动恢复 notify/notifyAll 或超时

面试一句话:

sleep 是线程休眠,不释放锁;wait 是对象等待,会释放当前对象锁,必须配合 synchronized 使用。


4. start vs run

对比 start run
是否创建新线程
执行方式 JVM 创建新线程后调用 run 普通方法调用
场景 启动线程 不直接用于启动线程

面试一句话:

调用 start() 才是真正启动一个新线程;直接调用 run() 只是普通方法调用,不会开启新线程。


5. Runnable vs Callable

对比 Runnable Callable
返回值 没有
异常 不能直接抛受检异常 可以抛异常
方法 run call
搭配 Thread / Executor Future / ExecutorService

面试一句话:

Runnable 没有返回值,Callable 有返回值,并且可以抛异常,通常配合 Future 获取异步执行结果。


6. execute vs submit

对比 execute submit
所属 Executor ExecutorService
返回值 返回 Future
异常表现 异常直接抛到线程的 UncaughtExceptionHandler 异常封装到 Future,get 时抛出
适合 不关心结果 关心执行结果

面试一句话:

execute 只提交任务,不返回结果;submit 会返回 Future,可以通过 get() 获取结果或异常。


7. CountDownLatch vs CyclicBarrier vs Semaphore

对比 CountDownLatch CyclicBarrier Semaphore
作用 等多个线程完成 多个线程互相等待 控制并发许可数
是否可复用 不可复用 可复用 可复用
典型场景 主线程等子任务完成 多线程分阶段同步 限流、连接池

面试一句话:

CountDownLatch 是一个线程等多个线程完成;CyclicBarrier 是多个线程互相等待到齐再继续;Semaphore 是控制同时访问资源的线程数量。


8. ThreadLocal vs InheritableThreadLocal

对比 ThreadLocal InheritableThreadLocal
作用 线程内部变量副本 父线程传给子线程
子线程是否能拿到父线程值 不能 可以
线程池场景 要注意 remove 线程池中可能脏数据
常见场景 用户上下文、traceId 父子线程上下文传递

面试一句话:

ThreadLocal 是线程隔离变量,子线程默认拿不到父线程的值;InheritableThreadLocal 可以在线程创建时继承父线程值,但在线程池复用场景下容易出现脏数据,实际可以用 TTL 这类方案。


二、集合相关对比

9. ArrayList vs LinkedList

对比 ArrayList LinkedList
底层 动态数组 双向链表
随机访问 O(1) O(n)
中间插入删除 需要移动元素 找到节点后改指针
内存占用 较低 较高,有 prev/next
适合 查询多 头尾操作多

面试一句话:

ArrayList 底层是数组,随机访问快;LinkedList 底层是双向链表,按下标查询慢,但头尾插入删除方便。


10. HashMap vs Hashtable vs ConcurrentHashMap

对比 HashMap Hashtable ConcurrentHashMap
线程安全 不安全 安全 安全
加锁方式 无锁 方法级 synchronized JDK8 CAS + synchronized
null key/value 支持一个 null key 不支持 不支持
性能 单线程高 较低 并发场景高

面试一句话:

HashMap 线程不安全;Hashtable 通过方法级 synchronized 保证安全,锁粒度粗;ConcurrentHashMap 是并发容器,JDK8 中主要通过 CAS 和桶级 synchronized 保证并发安全。


11. HashMap vs TreeMap

对比 HashMap TreeMap
底层 哈希表 红黑树
是否有序 无序 按 key 排序
查询复杂度 平均 O(1) O(log n)
是否支持范围查询 不适合 支持

面试一句话:

HashMap 适合快速查找;TreeMap 基于红黑树,适合需要 key 有序或范围查询的场景。


12. HashSet vs TreeSet

对比 HashSet TreeSet
底层 HashMap TreeMap
是否有序 无序 有序
性能 平均 O(1) O(log n)
去重依据 hashCode + equals compareTo / Comparator

面试一句话:

HashSet 基于哈希去重,性能高但无序;TreeSet 基于红黑树,能排序,但性能相对低。


三、字符串相关对比

13. String vs StringBuilder vs StringBuffer

对比 String StringBuilder StringBuffer
是否可变 不可变 可变 可变
线程安全 安全,因为不可变 不安全 安全
加锁 方法加 synchronized
适合 少量字符串 单线程大量拼接 多线程共享拼接

面试一句话:

String 不可变,每次修改都会产生新对象;StringBuilder 可变但线程不安全,性能高;StringBuffer 可变且线程安全,方法上加了 synchronized


14. == vs equals

对比 == equals
基本类型 比较值 不能直接用
引用类型 比较地址 默认比较地址,可重写比较内容
String 比较引用 比较字符串内容

面试一句话:

== 对基本类型比较值,对引用类型比较地址;equals 默认也是比较地址,但很多类比如 String 重写了 equals,用来比较内容。


15. final vs finally vs finalize

对比 final finally finalize
类型 关键字 异常处理代码块 Object 方法
作用 修饰类、方法、变量 保证资源清理逻辑执行 GC 前可能调用
是否推荐 推荐 推荐用于清理 不推荐,已过时思路

面试一句话:

final 表示不可变或不能继承/重写;finally 用于异常处理中的资源释放;finalize 是对象被回收前可能调用的方法,不可靠,实际开发不建议依赖。


四、面向对象相关对比

16. 重载 vs 重写

对比 重载 Overload 重写 Override
发生位置 同一个类 父子类
方法名 相同 相同
参数列表 必须不同 必须相同
返回值 不能仅靠返回值区分 相同或协变返回
多态 编译时多态 运行时多态

面试一句话:

重载是同一个类中方法名相同、参数不同,编译期确定;重写是子类改写父类方法,运行期根据真实对象类型确定调用哪个方法。


17. 抽象类 vs 接口

对比 抽象类 接口
继承关系 单继承 多实现
表达含义 是什么 能做什么
成员变量 可以有普通成员变量 默认 public static final
方法 可以有普通方法和抽象方法 可有抽象方法、默认方法、静态方法
构造方法 没有

面试一句话:

抽象类更适合表达一类对象的公共父类,强调“是什么”;接口更适合定义能力和规范,强调“能做什么”。


18. 普通类 vs 抽象类

对比 普通类 抽象类
是否能实例化 不能
是否能有抽象方法 不能
作用 直接创建对象 提供模板和约束
典型场景 具体业务类 模板方法、公共父类

面试一句话:

抽象类不能直接实例化,主要用于抽取公共逻辑和定义子类必须实现的行为。


19. this vs super

对比 this super
指向 当前对象 父类部分
调用构造 this(...) super(...)
调用成员 当前类成员 父类成员
使用场景 区分成员变量和局部变量 调用父类构造或被覆盖方法

面试一句话:

this 表示当前对象,super 表示父类对象部分,常用于调用父类构造方法或父类被重写的方法。


五、Spring 相关对比

20. @Autowired vs @Resource

对比 @Autowired @Resource
来源 Spring JSR-250 / Jakarta 标准
默认注入方式 按类型 按名称
配合注解 @Qualifier name 属性
是否 Spring 独有 否,Spring 支持它

面试一句话:

@Autowired 是 Spring 的注解,默认按类型注入;@Resource 是 Java/Jakarta 标准注解,默认按名称注入,找不到名称时再按类型匹配。


21. @Component vs @Bean

对比 @Component @Bean
作用位置 类上 方法上
创建方式 Spring 扫描后自动创建 调用方法,把返回值放进容器
适合 自己写的类 第三方类、复杂初始化对象
控制能力 较弱 更强

面试一句话:

@Component 用在类上,适合把自己写的类交给 Spring;@Bean 用在方法上,适合把第三方对象或复杂初始化对象交给 Spring。


22. @Controller vs @RestController

对比 @Controller @RestController
用途 返回页面或数据 返回 JSON 数据
是否包含 @ResponseBody 不包含 包含
典型场景 MVC 页面 前后端分离接口

面试一句话:

@RestController = @Controller + @ResponseBody,默认把方法返回值写入 HTTP 响应体,常用于返回 JSON。


23. BeanFactory vs ApplicationContext

对比 BeanFactory ApplicationContext
定位 基础 IOC 容器 完整应用上下文
功能 创建和管理 Bean IOC + AOP + 事件 + 国际化 + 资源加载
初始化 懒加载为主 默认启动时创建单例 Bean
使用场景 Spring 底层 实际开发

面试一句话:

BeanFactory 是 Spring 最基础的 IOC 容器;ApplicationContext 是它的增强版,提供事件、国际化、资源加载、AOP 等功能,实际开发中基本使用 ApplicationContext


24. BeanFactory vs FactoryBean

对比 BeanFactory FactoryBean
是什么 容器 一个特殊 Bean
作用 管理 Bean 自定义 Bean 创建逻辑
典型场景 Spring 容器核心接口 MyBatis Mapper 代理对象创建

面试一句话:

BeanFactory 是 Spring 容器;FactoryBean 是一种特殊 Bean,用来封装复杂对象的创建过程。


25. JDK 动态代理 vs CGLIB

对比 JDK 动态代理 CGLIB
底层 反射 + InvocationHandler 字节码增强 + 继承
是否需要接口 需要 不需要
代理方式 生成接口实现类 生成目标类子类
final 类/方法 不影响接口代理 final 不能代理

面试一句话:

JDK 动态代理基于接口,CGLIB 基于继承。Spring AOP 原生默认有接口用 JDK 动态代理,没有接口用 CGLIB;Spring Boot 默认通常倾向 CGLIB。


26. Spring MVC Interceptor vs Filter

对比 Filter Interceptor
所属 Servlet 规范 Spring MVC
执行位置 DispatcherServlet 之前 DispatcherServlet 之后、Controller 之前
拦截范围 所有 Web 请求 Spring MVC 请求
能否拿到 Handler 不方便 可以
典型场景 编码、跨域、安全过滤 登录、权限、日志、traceId

面试一句话:

Filter 是 Servlet 层面的过滤器,执行更靠前;Interceptor 是 Spring MVC 层面的拦截器,能拿到具体 handler,更适合做业务拦截。


六、JVM / 类加载相关对比

27. 堆 vs 栈

对比
存什么 对象实例、数组 方法调用栈帧、局部变量
线程共享 共享 线程私有
GC 主要区域 方法结束自动出栈
异常 OOM StackOverflowError

面试一句话:

堆是线程共享的,主要存对象;栈是线程私有的,存方法调用过程中的栈帧和局部变量。


28. 方法区 vs 元空间

对比 方法区 元空间
含义 JVM 规范中的逻辑区域 HotSpot 对方法区的实现
JDK7 永久代实现
JDK8+ 元空间实现 使用本地内存
存储 类元信息、常量、静态变量引用等 类元信息为主

面试一句话:

方法区是 JVM 规范概念,JDK8 以后 HotSpot 用元空间实现方法区,元空间使用本地内存,不再使用永久代。


29. 类加载器双亲委派 vs 打破双亲委派

对比 双亲委派 打破双亲委派
默认机制 先让父加载器加载 子加载器优先或自定义加载
目的 防止核心类被篡改 满足隔离、热部署、SPI
典型 java.lang.String 由 Bootstrap 加载 Tomcat、JDBC SPI

面试一句话:

双亲委派是类加载时先委托父加载器,保证核心类安全;Tomcat、SPI 等场景会打破双亲委派,实现类隔离或反向加载。


七、异常相关对比

30. Exception vs Error

对比 Exception Error
含义 程序可处理异常 JVM 或系统级严重错误
是否建议捕获 可以 一般不建议
例子 IOException、SQLException OOM、StackOverflowError

面试一句话:

Exception 通常是程序可处理的异常;Error 表示严重问题,比如 OOM、栈溢出,一般不建议业务代码捕获。


31. checked exception vs unchecked exception

对比 checked exception unchecked exception
编译期检查
是否必须处理 必须 catch 或 throws 不强制
例子 IOException NullPointerException

面试一句话:

受检异常编译期必须处理;非受检异常一般是运行期异常,不强制捕获。


八、IO 相关对比

32. BIO vs NIO vs AIO

| 对比 | BIO | NIO | AIO |
|---|---|---|
| 模型 | 同步阻塞 | 同步非阻塞 | 异步非阻塞 |
| 线程模型 | 一个连接一个线程 | 一个线程处理多个连接 | 操作系统完成后回调 |
| 核心 | Stream | Channel、Buffer、Selector | CompletionHandler |
| 场景 | 连接少 | 高并发网络 | 异步 IO 场景 |

面试一句话:

BIO 是同步阻塞,一个连接通常占一个线程;NIO 是同步非阻塞,通过 Selector 多路复用;AIO 是异步 IO,操作系统完成后回调通知。


33. InputStream vs Reader

对比 InputStream Reader
处理单位 字节 字符
适合 图片、文件、二进制 文本
编码处理 不处理字符编码 处理字符编码

面试一句话:

InputStream 面向字节,适合二进制数据;Reader 面向字符,适合文本数据,会涉及字符编码。

更多推荐