一、并发相关对比

1. synchronized vs ReentrantLock

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

面试一句话:

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


2. volatile vs synchronized

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

面试一句话:

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


3. sleep vs wait

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

面试一句话:

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


4. start vs run

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

面试一句话:

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


5. Runnable vs Callable

对比RunnableCallable
返回值没有
异常不能直接抛受检异常可以抛异常
方法runcall
搭配Thread / ExecutorFuture / ExecutorService

面试一句话:

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


6. execute vs submit

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

面试一句话:

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


7. CountDownLatch vs CyclicBarrier vs Semaphore

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

面试一句话:

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


8. ThreadLocal vs InheritableThreadLocal

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

面试一句话:

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


二、集合相关对比

9. ArrayList vs LinkedList

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

面试一句话:

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


10. HashMap vs Hashtable vs ConcurrentHashMap

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

面试一句话:

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


11. HashMap vs TreeMap

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

面试一句话:

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


12. HashSet vs TreeSet

对比HashSetTreeSet
底层HashMapTreeMap
是否有序无序有序
性能平均 O(1)O(log n)
去重依据hashCode + equalscompareTo / Comparator

面试一句话:

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


三、字符串相关对比

13. String vs StringBuilder vs StringBuffer

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

面试一句话:

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


14. == vs equals

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

面试一句话:

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


15. final vs finally vs finalize

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

面试一句话:

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


四、面向对象相关对比

16. 重载 vs 重写

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

面试一句话:

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


17. 抽象类 vs 接口

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

面试一句话:

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


18. 普通类 vs 抽象类

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

面试一句话:

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


19. this vs super

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

面试一句话:

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


五、Spring 相关对比

20. @Autowired vs @Resource

对比@Autowired@Resource
来源SpringJSR-250 / Jakarta 标准
默认注入方式按类型按名称
配合注解@Qualifiername 属性
是否 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

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

面试一句话:

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


24. BeanFactory vs FactoryBean

对比BeanFactoryFactoryBean
是什么容器一个特殊 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

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

面试一句话:

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


六、JVM / 类加载相关对比

27. 堆 vs 栈

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

面试一句话:

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


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

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

面试一句话:

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


31. checked exception vs unchecked exception

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

面试一句话:

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


八、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

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

面试一句话:

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

更多推荐