一、基础核心(必问)
1. Java 中的值传递和引用传递有什么区别?
答案:Java 中只有值传递,不存在引用传递:
- 值传递:方法接收的是实参的拷贝,方法内对参数的修改不会影响原实参(如基本数据类型
int、char等)。 - 引用传递(误区):对于对象类型,方法接收的是对象引用的拷贝(值),如果修改的是引用指向的对象内容(如对象属性),会影响原对象;但如果直接修改引用本身(如重新
new一个对象),不会影响原引用。
示例代码:
java
运行
public class PassTest { // 基本类型:值传递 public static void changeInt(int a) { a = 10; } // 对象类型:传递引用的拷贝 public static void changeObj(User user) { user.setName("新名字"); // 修改对象内容,影响原对象 user = new User("无关"); // 修改引用本身,不影响原引用 } static class User { private String name; public User(String name) { this.name = name; } // getter/setter 省略 } public static void main(String[] args) { int num = 1; changeInt(num); System.out.println(num); // 输出 1(未改变) User u = new User("旧名字"); changeObj(u); System.out.println(u.getName()); // 输出 新名字(对象内容被改) } }2. == 和 equals () 的区别?
答案:
==:- 比较基本类型:判断值是否相等;
- 比较引用类型:判断引用是否指向同一个对象(内存地址是否相同)。
equals():- 是
Object类的方法,默认实现等价于==(比较地址); - 常用类(如
String、Integer)重写了equals(),改为比较内容; - 自定义类如需按内容比较,需重写
equals()(建议同时重写hashCode())。
- 是
3. String、StringBuffer、StringBuilder 的区别?
| 特性 | String | StringBuffer | StringBuilder |
|---|---|---|---|
| 可变性 | 不可变(final) | 可变 | 可变 |
| 线程安全 | 安全(不可变) | 安全(同步方法) | 不安全 |
| 性能 | 低(频繁创建) | 中 | 高 |
| 适用场景 | 少量字符串操作 | 多线程字符串操作 | 单线程字符串操作 |
二、面向对象(核心)
1. 接口和抽象类的区别?
| 维度 | 抽象类 | 接口 |
|---|---|---|
| 继承 / 实现 | 单继承 | 多实现 |
| 方法 | 可含抽象 / 非抽象方法 | JDK8 + 可含默认 / 静态方法,其余抽象 |
| 成员变量 | 可含任意变量 | 只能是public static final |
| 构造方法 | 有 | 无 |
| 设计理念 | 体现 "is-a"(继承) | 体现 "has-a"(能力) |
2. 重载(Overload)和重写(Override)的区别?
- 重载:
- 发生在同一个类中;
- 方法名相同,参数列表(个数 / 类型 / 顺序)不同;
- 返回值、访问修饰符不影响重载;
- 编译期确定(静态绑定)。
- 重写:
- 发生在父子类中;
- 方法名、参数列表、返回值(协变)完全相同;
- 子类方法访问修饰符不能比父类更严格;
- 不能重写
final、static方法; - 运行期确定(动态绑定)。
三、JVM(高频)
1. JVM 内存模型(运行时数据区)?
JVM 运行时数据区分为 5 个部分:
- 程序计数器:线程私有,记录当前线程执行的字节码行号,无 OOM;
- 虚拟机栈:线程私有,存储方法栈帧(局部变量、操作数栈等),栈溢出(StackOverflowError)或 OOM;
- 本地方法栈:线程私有,为 Native 方法服务,同虚拟机栈;
- 堆:线程共享,存储对象实例,GC 核心区域,OOM 高发区(分新生代、老年代);
- 方法区:线程共享,存储类信息、常量、静态变量等,JDK8 后改为元空间(本地内存),仍可能 OOM。
2. GC 垃圾回收的判断方法?
- 引用计数法:给对象加引用计数器,引用 + 1,释放 - 1,计数器为 0 则回收;缺点:无法解决循环引用。
- 可达性分析算法:以 GC Roots(如虚拟机栈引用、静态变量、本地方法栈引用)为起点,遍历对象引用链,不可达的对象标记为可回收。
3. 常见的 GC 收集器?
- Serial GC:单线程,适合小内存、单核心(客户端);
- Parallel GC:多线程,注重吞吐量(默认 JVM 收集器);
- CMS GC:并发标记清除,注重低延迟,缺点:内存碎片、CPU 占用高;
- G1 GC:分区收集,兼顾吞吐量和延迟,适合大内存(JDK9 默认)。
四、并发编程(高频)
1. synchronized 和 Lock 的区别?
| 维度 | synchronized | Lock(如 ReentrantLock) |
|---|---|---|
| 实现方式 | JVM 层面(关键字) | JDK 层面(接口) |
| 释放锁 | 自动释放(异常 / 方法结束) | 手动释放(finally) |
| 可中断 | 不可中断 | 可中断(lockInterruptibly) |
| 超时获取锁 | 不支持 | 支持(tryLock (time)) |
| 公平锁 | 非公平 | 可指定公平 / 非公平 |
| 条件变量 | 单一 | 多条件(Condition) |
2. 线程池的核心参数(ThreadPoolExecutor)?
核心参数共 7 个,关键 5 个:
corePoolSize:核心线程数(常驻线程);maximumPoolSize:最大线程数;keepAliveTime:非核心线程空闲超时时间;workQueue:任务队列(如 ArrayBlockingQueue、LinkedBlockingQueue);threadFactory:线程创建工厂;handler:拒绝策略(如 AbortPolicy、CallerRunsPolicy)。
五、集合(必问)
1. HashMap 的底层实现(JDK8)?
- 底层:数组 + 链表 + 红黑树(链表长度≥8 且数组长度≥64 时转红黑树);
- 哈希计算:
hash(key) = key.hashCode() ^ (key.hashCode() >>> 16),再与数组长度 - 1 取模; - 扩容:默认初始容量 16,负载因子 0.75,扩容为 2 倍,重新哈希;
- 线程安全:非线程安全,并发场景用 ConcurrentHashMap(JDK8 用 CAS+synchronized)。
2. ArrayList 和 LinkedList 的区别?
| 维度 | ArrayList | LinkedList |
|---|---|---|
| 底层实现 | 动态数组 | 双向链表 |
| 随机访问 | 快(O (1)) | 慢(O (n)) |
| 增删操作 | 慢(需移动元素) | 快(仅改指针) |
| 内存占用 | 连续内存,有扩容冗余 | 每个节点存前后指针,占用更多 |
| 适用场景 | 大量读取、少量增删 | 大量增删、少量读取 |
总结
- 基础核心:重点掌握值传递 / 引用传递、==/equals、String 相关、OOP 特性(封装 / 继承 / 多态);
- 高频进阶:JVM 内存模型、GC 机制、HashMap 实现、线程池、并发锁(synchronized/Lock)是面试重中之重;
- 答题技巧:回答时先给出核心结论,再补充细节 / 示例,结合使用场景说明(如 "HashMap 非线程安全,所以高并发下用 ConcurrentHashMap")。