跳到主要内容Java 核心面试题与实战解析:从基础到 JVM | 极客日志Javajava算法
Java 核心面试题与实战解析:从基础到 JVM
Java 面试考察范围广泛,涵盖基础语法、集合、并发、JVM 及 Spring 框架等核心模块。本文整理了高频考点,详解 HashMap 底层结构、线程池参数调优、类加载双亲委派模型及 Spring AOP 原理,配合代码示例剖析易错点,助力开发者夯实基础,从容应对技术挑战。
花里胡哨1 浏览 Java 核心面试题与实战解析
一、Java 基础语法与核心特性
1. Java 的核心特性有哪些?
解析:
- 跨平台性:通过 JVM 实现,字节码文件可在任意支持 JVM 的操作系统运行(Write Once, Run Anywhere)。
- 面向对象:封装、继承、多态三大核心特性。
- 安全性:支持沙箱机制、字节码校验、权限控制(如文件 IO 权限)。
- 健壮性:自动垃圾回收(GC)避免内存泄漏,强类型检查、异常处理机制减少运行时错误。
- 分布式:支持 RMI、HTTP 协议,便于开发分布式应用。
- 多线程:内置多线程 API,支持并发编程。
2. 基本数据类型与包装类的区别?
| 维度 | 基本数据类型(如 int、float) | 包装类(如 Integer、Float) |
|---|
| 本质 | 原始值,无对象属性 | 引用类型,继承 Object 类 |
| 默认值 | 有(如 int 默认 0,boolean 默认 false) | 无,默认 null |
| 适用场景 | 简单运算、局部变量,效率高 | 集合框架(如 List)、泛型、需要 null 值的场景 |
| 缓存机制 | 无 | 部分包装类(Integer[-128~127]、Byte、Short 等)有常量池缓存 |
关键考点:
- 自动装箱/拆箱:Java 5+ 特性,编译器自动完成基本类型与包装类的转换。
- 缓存陷阱:
Integer a = 127; Integer b = 127; → a == b为 true(复用缓存);Integer c = 128; Integer d = 128; → c == d为 false(新建对象),需用 equals() 比较值。
3. String、StringBuffer、StringBuilder 的区别?
解析: 核心差异在于可变性和线程安全:
- String:不可变(底层是 final 修饰的 char 数组/JDK9+ byte 数组),每次修改都会创建新对象,效率低。
- StringBuffer:可变,线程安全(方法加 synchronized 锁),适用于多线程环境的字符串拼接。
- StringBuilder:可变,线程不安全,效率高于 StringBuffer,适用于单线程环境的字符串拼接。
底层原理:
String 的不可变性源于 private final char value[](JDK8)。StringBuffer 和 StringBuilder 继承 AbstractStringBuilder,底层是可变 char 数组,扩容机制为:默认初始容量 16,当长度超过容量时,新容量=原容量×2+2,不足则直接扩容到所需长度。
4. final 关键字的三种用法?
- 修饰类:类不可被继承(如 String、Math),子类无法扩展其功能。
修饰方法:方法不可被重写,可防止子类修改父类核心逻辑。修饰变量:变量不可被重新赋值(基本类型:值不可变;引用类型:引用地址不可变,但对象内容可修改)。易错点:
final int[] arr = {1,2,3}; arr[0] = 4; 合法(数组内容可变);arr = new int[5]; 非法(引用地址不可变)。
5. 接口(Interface)与抽象类(Abstract Class)的区别?
| 维度 | 接口(Interface) | 抽象类(Abstract Class) |
|---|
| 继承方式 | 多实现(一个类可实现多个接口) | 单继承(一个类只能继承一个抽象类) |
| 成员变量 | 只能是 public static final 常量 | 可包含普通变量、静态变量、常量 |
| 成员方法 | JDK8 前:只能是抽象方法;JDK8+:支持 default/static 方法;JDK9+:支持 private 方法 | 可包含抽象方法、普通方法、静态方法 |
| 构造方法 | 无 | 有(不能实例化,供子类调用) |
| 设计目的 | 定义行为规范,解耦(如 List 接口) | 定义类的模板,复用代码(如 HttpServlet) |
- 接口:不同类需统一行为但实现不同(如 Runnable 接口)。
- 抽象类:同类组件共享核心逻辑(如 AbstractList 封装 List 的公共方法)。
6. Java 异常体系的核心结构?
- 顶层父类:
Throwable,包含两个核心子类:
Error:严重错误(如 OutOfMemoryError、StackOverflowError),程序无法恢复,无需捕获。
Exception:可处理的异常,分为受检异常(Checked Exception)和非受检异常(Unchecked Exception)。
try:包裹可能抛出异常的代码。
catch:捕获并处理异常(可多个 catch,按异常子类→父类顺序)。
finally:无论是否抛出异常,都会执行(常用于关闭资源)。
throw:手动抛出异常。
throws:声明方法可能抛出的异常。
- 避免捕获
Throwable(包含 Error,无法处理)。
- 不要忽略异常(空 catch 块)。
- 优先使用 try-with-resources 自动关闭资源(JDK7+)。
二、Java 集合框架
1. 集合框架的核心接口与继承关系?
Java 集合框架核心分为两大体系(均位于 java.util 包):
- 单列集合(Collection):存储单个元素,核心子接口:
List(有序、可重复)、Set(无序、不可重复)。
- 双列集合(Map):存储键值对(key-value),核心实现类:HashMap、TreeMap、LinkedHashMap、ConcurrentHashMap。
List:支持索引访问,可通过 get(int index) 获取元素。
Set:基于 equals() 和 hashCode() 保证元素唯一性。
Map:key 不可重复,value 可重复;JDK8+ 中 Map 提供 forEach()、computeIfAbsent() 等便捷方法。
2. ArrayList 与 LinkedList 的区别?
| 维度 | ArrayList(数组实现) | LinkedList(双向链表实现) |
|---|
| 底层结构 | 动态数组(Object[]) | 双向链表(每个节点存储 prev、next、value) |
| 访问效率 | 随机访问快(O(1)) | 随机访问慢(O(n)),需遍历链表 |
| 增删效率 | 尾部增删快(O(1)),中间增删慢(O(n)) | 中间增删快(O(1)),尾部增删需遍历(O(n)) |
| 内存占用 | 连续内存,占用少 | 非连续内存,每个节点有额外指针开销 |
| 线程安全 | 不安全 | 不安全 |
- ArrayList:频繁查询、少量增删(如数据展示列表)。
- LinkedList:频繁中间增删、队列/栈实现(如消息队列)。
3. HashMap 的底层实现原理(JDK1.7 vs JDK1.8)?
HashMap 是基于'哈希表'的 Map 实现,核心是'数组 + 链表/红黑树'的结构,目的是平衡查询和增删效率。
JDK1.7 实现:
- 底层:数组(Entry[])+ 单向链表。
- 存储流程:计算 key 的 hashCode() → 哈希扰动 → 定位数组索引 → 头插法插入链表(若冲突)。
JDK1.8 优化:
- 底层:数组(Node[])+ 单向链表 + 红黑树(链表长度≥8 且数组长度≥64 时转为红黑树)。
- 存储流程:尾插法插入链表(避免 JDK1.7 头插法导致的链表循环问题)。
- 其他优化:支持 null key 和 null value,扩容机制更灵活。
- 哈希扰动:减少哈希值的高位忽略问题,提升分布均匀性。
- 链地址法:冲突元素以链表/红黑树形式存储在同一索引位置。
线程安全问题:
HashMap 线程不安全。解决方案:使用 ConcurrentHashMap 或 Collections.synchronizedMap(new HashMap<>())。
4. ConcurrentHashMap 的线程安全实现(JDK1.7 vs JDK1.8)?
ConcurrentHashMap 是 HashMap 的线程安全版本,核心差异在于锁机制:
JDK1.7 实现:
- 锁机制:分段锁(Segment 继承 ReentrantLock),每个 Segment 对应一把锁,支持 16 个线程并发。
JDK1.8 优化:
- 锁机制:放弃分段锁,采用'CAS + synchronized'实现线程安全。无冲突时 CAS 操作,有冲突时 synchronized 锁定当前链表/红黑树的头节点,并发度更高。
5. HashSet 的实现原理?
- 存储元素时:
add(E e) → 调用 HashMap 的 put(e, PRESENT),其中 PRESENT 是一个静态空 Object(仅占位)。
- 元素唯一性:依赖 HashMap 的 key 不可重复特性。
关键考点:
自定义对象作为 HashSet 元素时,必须重写 equals() 和 hashCode(),否则无法保证唯一性。
三、Java 多线程与并发
1. Java 创建线程的三种方式?
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread running");
}
}
new MyThread().start();
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable running");
}
}
new Thread(new MyRunnable()).start();
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "Callable result";
}
}
FutureTask<String> future = new FutureTask<>(new MyCallable());
new Thread(future).start();
String result = future.get();
- 继承 Thread:无法继承其他类,代码简单。
- 实现 Runnable/Callable:可继承其他类,支持多线程共享资源,Callable 支持返回值和异常处理。
2. 线程的生命周期与状态转换?
Java 线程有 6 种状态(定义在 Thread.State 枚举中):
NEW:线程创建后未启动。
RUNNABLE:线程启动后,处于可运行状态。
BLOCKED:线程等待同步锁。
WAITING:线程无限期等待。
TIMED_WAITING:线程限时等待。
TERMINATED:线程执行完成或异常终止。
3. synchronized 与 Lock 的区别?
| 维度 | synchronized(内置锁) | Lock(显式锁,如 ReentrantLock) |
|---|
| 锁实现 | JVM 层面实现(C++ 代码) | JDK 层面实现(Java 代码) |
| 锁获取与释放 | 自动获取、自动释放 | 手动获取(lock())、手动释放(unlock()) |
| 锁类型 | 可重入锁、非公平锁(默认) | 可重入锁,支持公平锁/非公平锁 |
| 功能扩展 | 无 | 支持中断锁、超时锁、条件变量 |
| 性能 | JDK6+ 优化后,性能接近 Lock | 高并发场景下性能更优,灵活度高 |
4. volatile 关键字的作用?
volatile 是 Java 提供的轻量级同步机制,核心作用有两个:
- 保证可见性:一个线程修改 volatile 变量后,其他线程能立即看到最新值。
- 禁止指令重排序:通过内存屏障阻止重排序(如 DCL 单例模式中防止空指针)。
- 不保证原子性:如
volatile int i = 0; i++; 非原子操作。
- 不能替代锁:仅适用于'单写多读'或'状态标记'场景。
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();
}
}
}
return instance;
}
}
5. 线程池的核心参数与工作原理?
Java 线程池核心类是 ThreadPoolExecutor,基于'池化思想'减少线程创建/销毁开销。
核心参数:
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 临时线程空闲时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务阻塞队列
ThreadFactory threadFactory, // 线程创建工厂
RejectedExecutionHandler handler // 拒绝策略
)
工作原理:
- 提交任务时,若核心线程数未满,创建核心线程执行任务。
- 核心线程满时,任务加入阻塞队列。
- 队列满时,若未达到最大线程数,创建临时线程执行任务。
- 临时线程空闲时间超过 keepAliveTime,销毁临时线程。
- 队列和最大线程数都满时,执行拒绝策略。
常见拒绝策略:
AbortPolicy(默认):抛出异常。
CallerRunsPolicy:由提交任务的线程执行。
DiscardPolicy:直接丢弃新任务。
DiscardOldestPolicy:丢弃最旧的任务。
注意: 阿里巴巴 Java 开发手册禁止使用 Executors 创建线程池,原因是队列无界可能导致 OOM 或 CPU 耗尽。推荐直接使用 ThreadPoolExecutor 构造方法,指定合理参数。
6. ThreadLocal 的原理与内存泄漏问题?
ThreadLocal 是线程本地存储工具,允许每个线程拥有独立的变量副本。
原理:
- 底层结构:每个 Thread 对象持有一个
ThreadLocalMap,key 是 ThreadLocal 实例(弱引用),value 是线程本地变量副本。
内存泄漏问题:
- 原因:key 是弱引用,当 ThreadLocal 实例被回收,key 变成 null,而 value 是强引用,若线程未结束(如线程池核心线程),value 无法被 GC 回收。
- 解决方案:用完 ThreadLocal 后调用
remove() 方法删除 value;避免使用静态 ThreadLocal。
四、JVM 核心原理
1. JVM 内存模型(运行时数据区)?
JVM 运行时数据区分为 5 个部分(基于 JDK8):
- 程序计数器:存储当前线程执行的字节码指令地址,线程私有。
- 虚拟机栈:存储线程执行方法时的栈帧,线程私有。
- 本地方法栈:支持 Native 方法的执行,线程私有。
- 堆(Heap):存储对象实例和数组,JVM 内存最大的区域,也是 GC 的主要区域,线程共享。
- 方法区(Method Area):存储类元信息、常量池、静态变量,JDK8 后被元空间替代。
2. 垃圾回收(GC)的核心原理?
1. 垃圾判定算法:
- 可达性分析算法:以'GC Roots'为起点,遍历对象引用链,不可达的对象判定为垃圾。
2. 常见 GC 算法:
- 标记 - 清除:产生内存碎片。
- 复制算法:适合年轻代,无内存碎片,利用率低。
- 标记 - 整理:适合老年代,无内存碎片,移动成本高。
- 分代收集:根据对象存活时间划分代,不同代采用不同算法。
3. 常见 GC 收集器:
- Serial:单线程,适合客户端。
- Parallel Scavenge:追求高吞吐量,适合服务器。
- CMS:追求低延迟,适合 Web 应用。
- G1:JDK9+ 默认,兼顾吞吐量和低延迟,支持大堆。
- ZGC/Shenandoah:新一代低延迟 GC。
3. 类加载机制与双亲委派模型?
1. 类加载流程:
2. 类加载器分类:
- 启动类加载器:加载 JDK 核心类库。
- 扩展类加载器:加载 jre/lib/ext 目录下的类库。
- 应用程序类加载器:加载应用 classpath 下的类库。
3. 双亲委派模型:
- 核心规则:先委托父加载器加载,父加载器无法加载时才由自身加载。
- 作用:避免类重复加载,保护核心类库。
五、Spring 核心框架
1. Spring IoC 的原理与实现?
IoC(Inversion of Control)指将对象的创建、依赖注入的控制权从应用程序转移到 Spring 容器。
依赖注入的三种方式:
- 字段注入:代码简洁,但不推荐(不利于单元测试)。
- Setter 方法注入:适用于可选依赖。
- 构造方法注入:推荐使用(强制依赖,避免空指针)。
IoC 容器初始化流程:
- 加载配置文件。
- 扫描 Bean 定义,注册到 BeanDefinitionRegistry。
- 实例化 Bean。
- 依赖注入。
- 初始化 Bean。
- 容器关闭时,销毁 Bean。
2. Spring AOP 的原理与应用?
AOP(Aspect-Oriented Programming)通过'横切'机制,将通用功能与业务逻辑解耦。
核心概念:
- 切面(Aspect):封装通用功能的类。
- 通知(Advice):切面的具体逻辑(@Before, @After, @Around 等)。
- 切入点(Pointcut):定义切面作用的目标方法。
实现原理:
- JDK 动态代理:目标类实现接口。
- CGLIB 动态代理:目标类未实现接口。
示例:日志切面
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(public * com.example.service..*(..))")
public void servicePointcut() {}
@Around("servicePointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("方法调用前");
Object result = joinPoint.proceed();
System.out.println("方法调用后");
return result;
}
}
3. Spring 事务管理的原理?
Spring 事务管理核心是'声明式事务'(基于 AOP)和'编程式事务'。
1. 事务的 ACID 特性:
2. 事务隔离级别:
READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE。
3. 事务传播行为:
- REQUIRED(默认):加入现有事务或创建新事务。
- REQUIRES_NEW:创建新事务,挂起当前事务。
4. 声明式事务实现:
通过 @Transactional 注解启用,底层通过 AOP 动态代理实现。
5. 事务失效的常见场景:
- 方法非 public 修饰。
- 事务方法内部调用(未经过代理类)。
- 异常类型不匹配。
- 手动捕获异常未抛出。
六、数据库与 MyBatis
1. JDBC 的核心操作步骤?
- 加载数据库驱动。
- 建立数据库连接。
- 创建 Statement/PreparedStatement 对象。
- 执行 SQL。
- 处理结果集。
- 关闭资源。
- PreparedStatement vs Statement:PreparedStatement 支持预编译 SQL,防止 SQL 注入,性能更优。
2. MyBatis 的核心组件与工作原理?
MyBatis 是持久层框架,简化 JDBC 操作。
核心组件:
- SqlSessionFactory:核心工厂类。
- SqlSession:会话对象。
- Mapper 接口:自定义 DAO 接口。
工作原理:
- 加载配置文件。
- 创建 SqlSessionFactory。
- 创建 SqlSession。
- 获取 Mapper 代理对象。
- 执行 SQL。
- 处理结果集。
3. MyBatis 的一级缓存与二级缓存?
- 一级缓存:SqlSession 级别,默认开启。同一个 SqlSession 内多次相同查询仅查一次数据库。
- 二级缓存:Mapper 级别,默认关闭。多个 SqlSession 共享缓存。需注意对象需实现 Serializable 接口。
七、设计模式与性能优化
1. 单例模式的几种实现方式与线程安全?
- 饿汉式:类加载时初始化,线程安全,非懒加载。
- 懒汉式:使用时才创建,需加锁或 volatile 保证线程安全。
- 静态内部类式:推荐,懒加载,线程安全,无锁开销。
- 枚举式:最佳实践,防反射/序列化,天然线程安全。
2. Java 性能优化的常见手段?
1. 代码层面优化:
- 集合初始化时指定初始容量。
- 频繁拼接用 StringBuilder。
- 减少循环内的对象创建。
2. JVM 层面优化:
- 合理设置堆内存配置。
- 调整年轻代比例。
- 选择合适的 GC 收集器。
3. 数据库层面优化:
- 索引优化。
- SQL 优化(避免 SELECT *)。
- 连接池优化。
4. 架构层面优化:
八、总结
本文覆盖 Java 面试核心考点,从基础语法、集合框架、多线程、JVM、Spring、数据库到设计模式与性能优化。面试时,除了记忆答案,更要理解底层原理,并结合项目经验说明实际应用场景。建议重点掌握多线程并发、JVM、Spring 核心原理等高级考点,这些是区分初级与中高级开发者的关键。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online