Appearance
Java 基础面试题
String 相关
Q1: String 为什么是不可变的?
java
// String 不可变的三个原因:
public final class String {
private final char[] value; // 1. private final 修饰
// 2. 没有提供 setter
// 3. 构造时拷贝副本
}答案:
private final char[] value- 字符数组被 final 修饰- 没有对外提供修改 value 的方法
- 构造方法中拷贝了传入的字符数组,防止外部修改
延伸:StringBuilder 和 StringBuffer 是可变的,因为没有 final 修饰。
Q2: String、StringBuilder、StringBuffer 区别?
| 对比 | String | StringBuilder | StringBuffer |
|---|---|---|---|
| 可变性 | 不可变 | 可变 | 可变 |
| 线程安全 | 安全 | 不安全 | 安全(synchronized) |
| 性能 | 每次修改创建新对象 | 高 | 中(需同步) |
| 使用场景 | 字符串不需修改 | 单线程字符串拼接 | 多线程字符串拼接 |
集合相关
Q3: ArrayList vs LinkedList 区别?
| 对比 | ArrayList | LinkedList |
|---|---|---|
| 底层 | 动态数组 | 双向链表 |
| 随机访问 | O(1) ⭐ | O(n) |
| 头部插入/删除 | O(n) | O(1) ⭐ |
| 尾部插入/删除 | O(1) 均摊 | O(1) |
| 内存占用 | 小(连续) | 大(节点+指针) |
Q4: HashMap 底层实现原理?
┌─────────────────────────────────────────────────────────────────┐
│ JDK 8+ HashMap 底层结构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 数组 + 链表 + 红黑树 │
│ │
│ 插入流程: │
│ 1. 计算 key 的 hash 值 │
│ 2. 计算数组索引位置 │
│ 3. 如果该位置为空,直接插入 │
│ 4. 如果不为空: │
│ - 链表长度 < 8 → 插入链表尾部 │
│ - 链表长度 >= 8 → 转为红黑树 │
│ │
└─────────────────────────────────────────────────────────────────┘Q5: ConcurrentHashMap 如何保证线程安全?
JDK 7:Segment 分段锁
java
// 每个 Segment 类似 HashMap,加锁
Segment<K,V>[] segments;JDK 8+:CAS + synchronized
java
// 数组为空 → CAS 插入
// 数组不为空 → synchronized 加锁处理多线程相关
Q6: synchronized 和 ReentrantLock 区别?
| 对比 | synchronized | ReentrantLock |
|---|---|---|
| 获取方式 | 自动获取/释放 | 手动获取/释放 |
| 尝试非阻塞 | 不支持 | tryLock() |
| 超时获取 | 不支持 | tryLock(timeout) |
| 中断等待 | 不支持 | lockInterruptibly() |
| 公平锁 | 不支持 | 可选公平/非公平 |
| 多条件 | 不支持 | newCondition() |
Q7: volatile 关键字作用?
答案:
- 可见性:一个线程修改后,其他线程立即可见
- 有序性:禁止指令重排序
不能保证:原子性(如 i++ 操作)
java
// volatile 不保证原子性示例
public class VolatileDemo {
volatile int i = 0;
public void inc() {
i++; // 不是原子操作
}
// 10个线程各执行1000次,i 不一定等于10000
}Q8: 线程池工作原理?
提交任务 → 核心线程池未满 → 创建核心线程执行
↓ (已满)
加入任务队列
↓ (队列满)
最大线程池未满 → 创建临时线程执行
↓ (已满)
执行拒绝策略JVM 相关
Q9: JVM 内存区域划分?
| 区域 | 线程共享 | 存储内容 | 异常 |
|---|---|---|---|
| Heap | 是 | 对象实例 | OOM |
| Method Area | 是 | 类信息、常量、静态变量 | OOM |
| JVM Stack | 否 | 局部变量、方法参数 | SOF |
| PC Register | 否 | 当前字节码行号 | - |
| Native Stack | 否 | native 方法 | SOF |
Q10: 什么情况下会触发 Full GC?
答案:
- 老年代空间不足
- System.gc()(不确定,由 JVM 决定)
- Metaspace 空间不足
- Young GC 时 Survivor 区放不下对象
Q11: 类加载双亲委派机制?
Bootstrap ClassLoader ← Extension ClassLoader ← Application ClassLoader ← 自定义ClassLoader
加载请求向上传递,父类无法加载才由子类加载好处:
- 安全性:核心类(如 java.lang.String)由 Bootstrap 加载,不会被替换
- 避免重复加载