1. Java面试手写代码题的重要性与核心考察点在Java技术岗位的面试中手写代码环节往往是区分候选人真实水平的关键门槛。我经历过上百场技术面试发现能流畅写出高质量代码的候选人实际工作能力通常都不会差。这10个经典题目涵盖了单例模式、排序算法和线程池等核心知识点正是面试官最常用来考察基本功的试金石。为什么企业如此看重手写代码能力首先这能直接检验你对Java基础的理解深度——知道概念和能写出正确实现完全是两回事。其次在无IDE提示的情况下编码能暴露你的编程习惯和思维严谨性。最后这些题目都来源于真实开发场景比如电商系统必须处理高并发下的单例安全性大数据处理离不开高效排序而线程池更是服务端开发的标配。2. 单例模式的双重校验锁实现与演进2.1 基础版懒汉式单例public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance null) { instance new Singleton(); } return instance; } }这个版本在多线程环境下会创建多个实例。我在早期面试中曾因此被扣分后来才明白synchronized的必要性。2.2 线程安全版单例public class Singleton { private static Singleton instance; private Singleton() {} public static synchronized Singleton getInstance() { if (instance null) { instance new Singleton(); } return instance; } }加锁保证了线程安全但每次获取实例都要同步性能堪忧。在QPS过万的系统中这种实现会成为瓶颈。2.3 双重校验锁终极版public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }这里有三个关键点volatile防止指令重排序导致的NPE外层判空避免不必要的锁竞争内层判空确保唯一实例注意JDK5的volatile语义才真正完善早期版本仍可能存在问题3. 排序算法的手写实现与优化3.1 快速排序的经典实现public void quickSort(int[] arr, int low, int high) { if (low high) { int pivot partition(arr, low, high); quickSort(arr, low, pivot - 1); quickSort(arr, pivot 1, high); } } private int partition(int[] arr, int low, int high) { int pivot arr[high]; int i low; for (int j low; j high; j) { if (arr[j] pivot) { swap(arr, i, j); i; } } swap(arr, i, high); return i; }实际面试时我遇到过要求优化递归深度的变体题。可以添加栈深度检查超过阈值转为堆排序。3.2 归并排序的边界处理public void mergeSort(int[] arr, int left, int right) { if (left right) { int mid left (right - left) / 2; mergeSort(arr, left, mid); mergeSort(arr, mid 1, right); merge(arr, left, mid, right); } } private void merge(int[] arr, int left, int mid, int right) { // 需要额外O(n)空间 int[] temp new int[right - left 1]; int i left, j mid 1, k 0; while (i mid j right) { temp[k] arr[i] arr[j] ? arr[i] : arr[j]; } while (i mid) temp[k] arr[i]; while (j right) temp[k] arr[j]; System.arraycopy(temp, 0, arr, left, temp.length); }有次面试被要求改写成非递归实现考察了对算法本质的理解。可以用队列模拟递归调用栈。4. 线程池的七大参数手写实现4.1 核心参数解析public class ThreadPoolExecutor implements Executor { private final BlockingQueueRunnable workQueue; private final SetWorker workers new HashSet(); private volatile int corePoolSize; private volatile int maximumPoolSize; private volatile long keepAliveTime; private volatile RejectedExecutionHandler handler; private volatile ThreadFactory threadFactory; // 构造方法省略... }阿里Java规范要求核心线程数按业务类型设置CPU密集型N1IO密集型2N队列优先选LinkedBlockingQueue拒绝策略推荐自定义记录日志后补偿4.2 Worker线程的生命周期管理private final class Worker implements Runnable { final Thread thread; Runnable firstTask; Worker(Runnable firstTask) { this.firstTask firstTask; this.thread threadFactory.newThread(this); } public void run() { runWorker(this); } void runWorker(Worker w) { Runnable task w.firstTask; w.firstTask null; while (task ! null || (task getTask()) ! null) { try { task.run(); } finally { task null; } } processWorkerExit(w); } }这里有个坑线程池关闭时核心线程也可能被回收。需要设置allowCoreThreadTimeOut(true)才会生效。5. 其他高频手写题目精讲5.1 生产者-消费者模式public class ProducerConsumer { private final QueueInteger queue new LinkedList(); private final int CAPACITY 10; public void produce() throws InterruptedException { synchronized (queue) { while (queue.size() CAPACITY) { queue.wait(); } queue.offer(1); queue.notifyAll(); } } public void consume() throws InterruptedException { synchronized (queue) { while (queue.isEmpty()) { queue.wait(); } queue.poll(); queue.notifyAll(); } } }注意要用while而不是if检查条件避免虚假唤醒问题。我在美团二面时就栽在这个细节上。5.2 LRU缓存实现class LRUCache { class DLinkedNode { int key; int value; DLinkedNode prev; DLinkedNode next; } private MapInteger, DLinkedNode cache new HashMap(); private DLinkedNode head, tail; private int capacity; public LRUCache(int capacity) { this.capacity capacity; head new DLinkedNode(); tail new DLinkedNode(); head.next tail; tail.prev head; } public int get(int key) { DLinkedNode node cache.get(key); if (node null) return -1; moveToHead(node); return node.value; } public void put(int key, int value) { // 实现省略... } }这个题目考察数据结构综合运用能力LinkedHashMap虽然能简化实现但面试官通常要求手写双向链表。6. 面试实战技巧与避坑指南代码规范阿里巴巴Java开发手册要求方法参数不超过5个行宽不超过120字符使用javadoc注释关键算法边界检查所有手写代码必须考虑空指针处理数值边界并发场景测试用例写完主动给出测试案例// 单例测试示例 Test public void testSingleton() { Singleton s1 Singleton.getInstance(); Singleton s2 Singleton.getInstance(); assertSame(s1, s2); }复杂度分析主动说明时间/空间复杂度快排平均O(nlogn)最坏O(n²)归并稳定O(nlogn)但需要额外空间我在快手三面时因为主动分析了线程池任务堆积时的内存占用问题给面试官留下了深刻印象。这些实战经验远比死记硬背八股文有价值。