1. Java面试核心要点解析
Java作为企业级开发的主流语言,其面试考察点往往集中在语言特性、JVM原理、并发编程和框架应用等维度。根据近三年一线互联网企业的实际面试反馈,候选人需要重点准备以下内容:
- 基础语法:自动装箱拆箱、字符串常量池、final关键字等看似简单却容易踩坑的知识点
- 集合框架:HashMap扩容机制、ConcurrentHashMap分段锁优化、ArrayList与LinkedList差异
- JVM体系:类加载过程、内存区域划分、GC算法对比、JIT编译优化
- 并发编程:synchronized锁升级、AQS实现原理、ThreadLocal内存泄漏防范
- 新特性:模块化系统、var局部变量、record记录类、模式匹配等JDK8-17的重要更新
提示:面试官常通过"为什么HashMap负载因子是0.75"这类问题考察候选人的深度思考能力,建议每个知识点都追问设计初衷。
1.1 高频算法题型实战
白板编程环节最常出现的三类题目及解题模板:
排序算法变形题
java复制// 快速排序变种:三向切分解决大量重复元素
public void quickSort3Way(int[] arr, int lo, int hi) {
if (hi <= lo) return;
int lt = lo, gt = hi;
int v = arr[lo];
int i = lo + 1;
while (i <= gt) {
if (arr[i] < v) swap(arr, lt++, i++);
else if (arr[i] > v) swap(arr, i, gt--);
else i++;
}
quickSort3Way(arr, lo, lt - 1);
quickSort3Way(arr, gt + 1, hi);
}
二叉树遍历应用
java复制// 非递归后序遍历(双栈法)
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<Integer> res = new LinkedList<>();
if (root == null) return res;
Deque<TreeNode> stack = new ArrayDeque<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
res.addFirst(node.val);
if (node.left != null) stack.push(node.left);
if (node.right != null) stack.push(node.right);
}
return res;
}
动态规划典型题
java复制// 股票买卖最佳时机(含冷冻期)
public int maxProfit(int[] prices) {
int n = prices.length;
int[][] dp = new int[n][3]; // 0-持有 1-冷冻 2-未持有
dp[0][0] = -prices[0];
for (int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][2] - prices[i]);
dp[i][1] = dp[i-1][0] + prices[i];
dp[i][2] = Math.max(dp[i-1][2], dp[i-1][1]);
}
return Math.max(dp[n-1][1], dp[n-1][2]);
}
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. JVM深度考察要点
2.1 内存模型精讲
Java内存区域划分示意图:
| 区域 | 线程共享 | 存储内容 | 异常类型 |
|---|---|---|---|
| 方法区 | 是 | 类信息、常量、静态变量 | OutOfMemoryError |
| 堆 | 是 | 对象实例 | OutOfMemoryError |
| 虚拟机栈 | 否 | 栈帧、局部变量表 | StackOverflowError |
| 本地方法栈 | 否 | Native方法服务 | StackOverflowError |
| 程序计数器 | 否 | 字节码行号指示器 | 无 |
对象创建全流程
- 类加载检查:检查new指令参数能否在常量池定位到类符号引用
- 内存分配:采用指针碰撞(Serial等带压缩整理的GC)或空闲列表(CMS等基于标记-清除的GC)
- 内存空间初始化:零值填充保证实例字段不赋初值即可直接使用
- 对象头设置:Mark Word(哈希码、GC分代年龄等)、类型指针(指向类元数据)
- 执行
方法:按照程序员意愿初始化对象
2.2 GC算法实战对比
三种主流垃圾收集器配置示例:
bash复制# G1调优参数(JDK8+默认)
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:InitiatingHeapOccupancyPercent=45
# ZGC低延迟配置(JDK15+)
-XX:+UseZGC
-XX:ZAllocationSpikeTolerance=5
-XX:ZCollectionInterval=120
# Shenandoah平衡配置(JDK12+)
-XX:+UseShenandoahGC
-XX:ShenandoahGCMode=iu
-XX:ShenandoahGuaranteedGCInterval=30000
GC日志分析要点:
code复制[GC pause (G1 Evacuation Pause) (young), 0.0234178 secs]
[Parallel Time: 22.3 ms, GC Workers: 8]
[GC Worker Start (ms): Min: 843.5, Avg: 843.6, Max: 843.7]
[Ext Root Scanning (ms): Min: 0.8, Avg: 1.3, Max: 2.1]
[Update RS (ms): Min: 0.0, Avg: 0.1, Max: 0.2]
[Processed Buffers: Min: 0, Avg: 1.1, Max: 2]
[Scan RS (ms): Min: 0.0, Avg: 0.1, Max: 0.2]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0]
[Object Copy (ms): Min: 19.8, Avg: 20.3, Max: 20.7]
[Termination (ms): Min: 0.0, Avg: 0.2, Max: 0.3]
[Termination Attempts: Min: 1, Avg: 1.0, Max: 1]
[GC Worker Other (ms): Min: 0.0, Avg: 0.1, Max: 0.1]
[GC Worker Total (ms): Min: 21.9, Avg: 22.1, Max: 22.3]
[GC Worker End (ms): Min: 865.7, Avg: 865.7, Max: 865.7]
[Code Root Fixup: 0.0 ms]
[Clear CT: 0.2 ms]
[Other: 0.9 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.5 ms]
[Ref Enq: 0.0 ms]
[Free CSet: 0.2 ms]
[Eden: 2048.0K(2048.0K)->0.0B(2048.0K) Survivors: 0.0B->1024.0K Heap: 2048.0K(10.0M)->1024.0K(10.0M)]
[Times: user=0.03 sys=0.01, real=0.02 secs]
3. 并发编程高阶考点
3.1 锁优化全解析
synchronized锁状态迁移路径:
- 无锁 → 偏向锁:当第一个线程访问时,CAS操作设置偏向线程ID
- 偏向锁 → 轻量级锁:当第二个线程尝试获取时,撤销偏向模式
- 轻量级锁 → 重量级锁:自旋超过阈值(默认10次)或等待线程数>1
AQS核心实现逻辑:
java复制// 自定义共享锁示例
class SharedLock {
private final Sync sync = new Sync(2);
private static class Sync extends AbstractQueuedSynchronizer {
Sync(int count) { setState(count); }
protected int tryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 || compareAndSetState(available, remaining))
return remaining;
}
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (compareAndSetState(current, next))
return true;
}
}
}
public void lock() { sync.acquireShared(1); }
public void unlock() { sync.releaseShared(1); }
}
3.2 并发容器源码要点
ConcurrentHashMap分段演进:
- JDK7:Segment数组+ReentrantLock
- JDK8:Node+CAS+synchronized
java复制// JDK8 putVal关键代码段
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
break; // CAS成功插入新节点
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f); // 协助扩容
else {
synchronized (f) { // 锁住链表头节点
if (tabAt(tab, i) == f) {
if (fh >= 0) {
// 链表插入处理...
}
else if (f instanceof TreeBin) {
// 红黑树处理...
}
}
}
}
}
addCount(1L, binCount);
return null;
}
4. 框架原理深度剖析
4.1 Spring循环依赖解决机制
三级缓存工作流程:
- 创建A对象(半成品)→ 放入三级缓存singletonFactories
- A填充属性时发现依赖B → 创建B对象
- B填充属性时发现依赖A → 从三级缓存获取A的ObjectFactory
- ObjectFactory.getObject() → 返回A的早期引用
- B初始化完成 → A完成属性注入 → 移除三级缓存,放入一级缓存
关键源码片段:
java复制// DefaultSingletonBeanRegistry
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
4.2 MyBatis执行流程拆解
SQL执行核心链路:
- SqlSessionFactoryBuilder解析配置文件 → 构建Configuration对象
- SqlSessionTemplate获取SqlSession → 通过Executor执行
- StatementHandler创建预处理语句 → ParameterHandler设置参数
- ResultSetHandler处理结果集 → 返回映射后的Java对象
插件拦截点实现:
java复制// 自定义分页插件示例
@Intercepts({
@Signature(type= Executor.class, method="query",
args={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class PageInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameter = args[1];
RowBounds rowBounds = (RowBounds) args[2];
if (rowBounds != RowBounds.DEFAULT) {
Executor executor = (Executor) invocation.getTarget();
BoundSql boundSql = ms.getBoundSql(parameter);
// 执行COUNT查询
String countSql = "SELECT COUNT(*) FROM (" + boundSql.getSql() + ") temp";
Number total = (Number) executor.query(
createCountStatement(ms, boundSql),
parameter,
RowBounds.DEFAULT,
ResultHandler.NO_RESULT_HANDLER
);
// 修改原始SQL
String pageSql = boundSql.getSql() + " LIMIT ?, ?";
args[0] = createPageStatement(ms, boundSql, pageSql);
args[2] = RowBounds.DEFAULT;
// 执行分页查询
List<?> result = (List<?>) invocation.proceed();
return new Page<>(result, total.intValue(), rowBounds.getOffset(), rowBounds.getLimit());
}
return invocation.proceed();
}
}
