Skip to content

Java 基础面试题

String 相关

Q1: String 为什么是不可变的?

java
// String 不可变的三个原因:
public final class String {
    private final char[] value;  // 1. private final 修饰

    // 2. 没有提供 setter
    // 3. 构造时拷贝副本
}

答案

  1. private final char[] value - 字符数组被 final 修饰
  2. 没有对外提供修改 value 的方法
  3. 构造方法中拷贝了传入的字符数组,防止外部修改

延伸StringBuilderStringBuffer 是可变的,因为没有 final 修饰。


Q2: String、StringBuilder、StringBuffer 区别?

对比StringStringBuilderStringBuffer
可变性不可变可变可变
线程安全安全不安全安全(synchronized)
性能每次修改创建新对象中(需同步)
使用场景字符串不需修改单线程字符串拼接多线程字符串拼接

集合相关

Q3: ArrayList vs LinkedList 区别?

对比ArrayListLinkedList
底层动态数组双向链表
随机访问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 区别?

对比synchronizedReentrantLock
获取方式自动获取/释放手动获取/释放
尝试非阻塞不支持tryLock()
超时获取不支持tryLock(timeout)
中断等待不支持lockInterruptibly()
公平锁不支持可选公平/非公平
多条件不支持newCondition()

Q7: volatile 关键字作用?

答案

  1. 可见性:一个线程修改后,其他线程立即可见
  2. 有序性:禁止指令重排序

不能保证:原子性(如 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 Stacknative 方法SOF

Q10: 什么情况下会触发 Full GC?

答案

  1. 老年代空间不足
  2. System.gc()(不确定,由 JVM 决定)
  3. Metaspace 空间不足
  4. Young GC 时 Survivor 区放不下对象

Q11: 类加载双亲委派机制?

Bootstrap ClassLoader ← Extension ClassLoader ← Application ClassLoader ← 自定义ClassLoader

加载请求向上传递,父类无法加载才由子类加载

好处

  1. 安全性:核心类(如 java.lang.String)由 Bootstrap 加载,不会被替换
  2. 避免重复加载