Java面试经验分享:字节跳动现场手撕算法+八股文+场景题
一、手撕算法环节(核心考察点)
题目示例:实现快速排序(要求时间复杂度$O(n \log n)$,空间复杂度$O(1)$原地排序)
public class QuickSort {
public static void sort(int[] arr) {
if (arr == null || arr.length < 2) return;
quickSort(arr, 0, arr.length - 1);
}
private static void quickSort(int[] arr, int left, int right) {
if (left >= right) return;
int pivot = partition(arr, left, right);
quickSort(arr, left, pivot - 1);
quickSort(arr, pivot + 1, right);
}
private static int partition(int[] arr, int left, int right) {
int pivot = arr[right]; // 基准值选取
int i = left;
for (int j = left; j < right; j++) {
if (arr[j] < pivot) {
swap(arr, i, j);
i++;
}
}
swap(arr, i, right);
return i;
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
考察重点:
- 边界处理:空数组/单元素直接返回
- 分区逻辑:双指针移动确保$O(n)$分区
- 递归终止条件:
left >= right
- 时间复杂度证明:最坏$O(n^2)$,平均$O(n \log n)$
二、Java八股文高频题
-
HashMap原理
- 结构:数组+链表/红黑树(阈值=8)
- 哈希冲突解决:链地址法
- 扩容机制:负载因子0.75,2倍扩容重新哈希
-
JVM内存模型
graph LR A[JVM] --> B[堆-对象实例] A --> C[栈-局部变量] A --> D[方法区-类信息] A --> E[程序计数器]
-
synchronized vs ReentrantLock
特性 synchronized ReentrantLock 锁获取方式 JVM隐式 API显式 公平锁 不支持 可配置 条件队列 单一 多Condition
三、场景设计题案例
题目:设计分布式ID生成器(要求高并发、全局唯一)
解决方案:
public class SnowflakeIdGenerator {
private final long twepoch = 1288834974657L; //起始时间戳
private final long workerIdBits = 5L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long sequenceBits = 12L;
private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public SnowflakeIdGenerator(long workerId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("Worker ID超限");
}
this.workerId = workerId;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException("时钟回拨");
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & ((1 << sequenceBits) - 1);
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << 22)
| (workerId << 17)
| sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
}
设计要点:
- 64位ID结构:
1位符号位 + 41位时间戳 + 5位机器ID + 5位数据中心ID + 12位序列号
- 解决时钟回拨:抛出异常或等待时钟同步
- QPS上限:$2^{12} = 4096$个ID/毫秒
四、面试建议
- 算法准备:重点掌握二叉树、DP、双指针类题目
- 原理深挖:所有八股文需能白板画图说明(如HashMap扩容示意图)
- 场景题方法论:先厘清约束条件(QPS/数据量/一致性要求),再选择技术方案
- 沟通技巧:对不确定的问题,展示分析过程比直接放弃更重要
实际面试中可能遇到组合题,例如:"如何用Redis优化上述ID生成器的性能?" 需结合缓存穿透防护(布隆过滤器)和批量ID预生成机制回答。