Java核心知识快速复习:30分钟搞定高频面试题
Java核心知识快速复习:30分钟搞定高频面试题
分类:review
摘要:面试前快速复习Java核心知识点,掌握高频面试题对提升面试通过率至关重要
引言
在Java工程师的面试中,无论是校招还是社招,核心知识点的考察永远是重中之重。很多开发者有着丰富的项目经验,但在面对基础原理性问题时往往因为平时“只顾低头拉车,忘了抬头看路”而痛失良机。所谓的“高频面试题”,实际上是对Java语言本质特征的提炼。
本文将跳出死记硬背的模式,用30分钟的时间,带你深入理解Java集合、并发编程、JVM内存管理这三大核心领域的底层原理。我们将通过源码分析、实际代码演示和场景落地,帮你构建起坚固的知识体系。
一、 集合框架:HashMap的底层奥秘
HashMap是面试中“当之无愧”的王者。理解HashMap不仅仅是为了回答“线程不安全”,更是为了展示对数据结构与哈希算法的深刻理解。
1.1 核心数据结构演变
在JDK 1.8之前,HashMap的实现是数组 + 链表。而在JDK 1.8中,为了解决哈希冲突导致链表过长从而降低查询效率的问题,引入了红黑树。
底层结构:Node<K,V>[] table
当链表长度超过8且数组长度超过64时,链表会转化为红黑树。这将查询操作的时间复杂度从 $O(n)$ 降低到了 $O(\log n)$。
1.2 扰动函数与哈希冲突
面试常问:为什么HashMap的容量必须是2的n次幂?
这涉及到哈希计算的均匀分布。HashMap在计算索引时,使用 (n - 1) & hash。如果 n 是2的幂,那么 n-1 的二进制表示全是1(例如16-1=15,即1111)。这与hash进行与运算,等价于取模运算 hash % n,但位运算效率远高于取模。
此外,HashMap并非直接使用key的hashCode,而是进行了“扰动”处理:
// JDK 1.8 HashMap hash方法源码逻辑
static final int hash(Object key) {
int h;
// 高16位异或低16位,目的是让高位的特征参与到低位运算中,减少冲突
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
1.3 实战代码:重写HashCode与Equals
在实际开发中,使用自定义对象作为Key时,必须重写 hashCode 和 equals 方法,否则会导致内存泄漏或查询失败。
import java.util.HashMap;
import java.util.Objects;
/**
* 演示自定义对象作为HashMap的Key时,正确重写hashCode和equals的重要性。
*/
class User {
private String id;
private String name;
public User(String id, String name) {
this.id = id;
this.name = name;
}
// 必须重写equals:比较对象内容而非内存地址
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) && Objects.equals(name, user.name);
}
// 必须重写hashCode:保证equals相等的对象hashCode必须相等
// 假如只重写equals不重写hashCode,两个逻辑相同的对象会计算出不同的hash值,
// 导致存入Map时存了两个,取的时候却取不到。
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
public class HashMapDemo {
public static void main(String[] args) {
HashMap<User, String> map = new HashMap<>();
User u1 = new User("001", "Alice");
// 存入对象
map.put(u1, "Admin");
// 此时如果u1的属性被修改,hashCode会发生变化,导致无法从map中移除或查询该对象
// u1.name = "Bob"; // 这是一个危险操作,会导致内存泄漏
// 正常查询
User queryUser = new User("001", "Alice");
System.out.println("查询结果: " + map.get(queryUser)); // 输出: Admin
}
}
二、 并发编程:线程池与锁机制
并发编程是区分初中级工程师与高级工程师的分水岭。重点在于理解线程池的参数配置以及锁的底层实现。
2.1 线程池的拒绝策略与参数配置
阿里巴巴Java开发手册强制规定:禁止使用Executors创建线程池。因为 FixedThreadPool 和 CachedThreadPool 允许请求的队列长度为 Integer.MAX_VALUE,可能导致OOM。
我们需要手动通过 ThreadPoolExecutor 创建,重点理解以下参数:
1. corePoolSize:核心线程数(常驻)。
2. maximumPoolSize:最大线程数(包含临时线程)。
3. workQueue:工作队列(存储待执行任务)。
任务提交流程:
任务来了 -> 核心线程数未满?创建线程 -> 满了?

浙公网安备 33010602011771号