简介:这是一份面向Java初学者与应届求职者的面试题汇总文档,覆盖从校园或培训阶段走向技术面试所需的核心考点,帮助读者在有限时间内系统梳理知识脉络、查漏补缺。资源以单个docx文件打包,压缩包约155KB,内容按十八个模块组织,包括Java基础、多线程、反射、对象拷贝、JavaWeb、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/SpringCloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis与JVM,共收录208道常见面试题并附参考答案。其中Java基础部分对JDK与JRE的区别、==与equals的差异、hashCode与equals的关系、final关键字作用等高频问题给出了代码示例与源码解读,便于理解底层原理。目前已有616人学习,适合需要一份权威面试清单、按模块逐项复习的初级Java人员参考。
1. 从一份 208 题的 Java 初级面试题库说起:它到底能帮你解决什么
如果你正在准备 Java 初级岗位的面试,或者刚带完一个培训班、想给学员找一份能直接背、能直接查、能直接对着代码验证的题库,那这份《Java 初级面试题汇总(带答案)》大概率能省掉你不少翻博客的时间。它把内容拆成 18 个模块——Java 基础、多线程、反射、对象拷贝、JavaWeb、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/SpringCloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM,一共 208 道题,每道题都配了答案,部分题还带了可运行的代码片段。
它适合两类人:一类是校招或转行刚入行的开发者,需要一份覆盖面够广的清单来查漏补缺;另一类是工作一两年、想跳槽但基础概念已经模糊的人,拿它当复习提纲。不适合指望靠背题拿高薪的人——初级题就是初级题,答案给的是结论和最小示例,深挖还得自己补。下面我按“怎么用、怎么验证、坑在哪”的顺序,把这份资源拆开讲。
2. 十八个模块怎么排优先级:从 Java 基础到 JVM 的复习路线
拿到一份 208 题的文档,最忌讳从第一页顺着背到最后一页。初级面试的提问密度是不均匀的,Java 基础、集合、多线程这三块几乎每场必问,Spring 和 MySQL 次之,Zookeeper、Kafka 这类中间件在初级岗里出现频率明显偏低。所以第一步不是背,是排优先级。
2.1 按面试出现频率给模块分三档
我把这 18 个模块按初级岗的实际提问频率分成三档,你可以对照自己的薄弱项决定投入时间:
| 档位 | 模块 | 建议投入 | 理由 |
|---|---|---|---|
| 高频 | Java 基础、多线程、集合(在基础模块内)、MySQL、Spring/Spring MVC | 60% 时间 | 几乎每场必问,且追问深 |
| 中频 | JVM、Redis、MyBatis、设计模式、异常、网络 | 30% 时间 | 常作为加分项或二面内容 |
| 低频 | RabbitMQ、Kafka、Zookeeper、Hibernate、SpringCloud、对象拷贝、反射 | 10% 时间 | 初级岗问得浅,知道概念即可 |
这个分档不是绝对的,取决于你投的岗位方向。比如投中间件团队,Kafka 和 Zookeeper 的权重就要往上提。但对大多数初级岗来说,把高频档吃透,比每个模块都浅尝一遍要划算得多。
2.2 用“概念—代码—追问”三步法过每一题
这份资源的答案是“结论式”的,比如“== 对于基本类型是值比较,对于引用类型是引用比较”。光背这句话,面试官换个问法你就卡壳。我一般会按三步过一道题:
第一步,读答案,把结论用自己的话复述一遍。第二步,把答案里的代码片段敲进 IDE 跑一遍,看输出是否和文档一致。第三步,自己给自己提一个追问。比如==和equals这题,追问就是“为什么 String 的 equals 返回 true 而自定义对象的 equals 返回 false”。
以文档里==和equals的示例为例,你可以直接建一个测试类验证:
public class EqualsTest { public static void main(String[] args) { String x = "string"; String y = "string"; String z = new String("string"); // x 和 y 都指向常量池中的同一个 "string" System.out.println(x == y); // true // z 是 new 出来的,在堆上另开辟了空间 System.out.println(x == z); // false // equals 被 String 重写为值比较 System.out.println(x.equals(y)); // true System.out.println(x.equals(z)); // true } }这段代码的关键在于理解 JVM 的内存分配:字面量"string"会进常量池,new String("string")则在堆上创建新对象。==比较的是引用地址,equals在 String 中被重写为逐字符比较。跑一遍这个例子,比背十遍结论都管用。
2.3 给每个模块配一个“最小验证工程”
光看文档容易产生“我懂了”的错觉。我的习惯是给每个高频模块建一个最小的 Maven 工程,把文档里的代码片段放进去跑。比如多线程模块,文档里提到了synchronized锁升级、ThreadLocal、线程池的七种创建方式,这些都可以写一个main方法验证。
以线程池为例,文档列出了newFixedThreadPool、newCachedThreadPool等创建方式,你可以写一段代码观察不同线程池的行为差异:
import java.util.concurrent.*; public class ThreadPoolDemo { public static void main(String[] args) throws Exception { // 固定 2 个线程,队列无界,任务超过 2 个会排队 ExecutorService fixed = Executors.newFixedThreadPool(2); for (int i = 0; i < 5; i++) { final int taskId = i; fixed.submit(() -> { System.out.println("fixed task " + taskId + " on " + Thread.currentThread().getName()); try { Thread.sleep(500); } catch (InterruptedException e) {} }); } fixed.shutdown(); fixed.awaitTermination(10, TimeUnit.SECONDS); // 缓存线程池,空闲 60 秒回收,适合大量短任务 ExecutorService cached = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) { final int taskId = i; cached.submit(() -> System.out.println("cached task " + taskId + " on " + Thread.currentThread().getName())); } cached.shutdown(); } }跑完你会直观看到:fixed池里始终只有两个线程名在交替,cached池则可能为每个任务开新线程。文档里说的“无界队列”“60 秒回收”这些参数,跑一遍就有体感了。参数上,newFixedThreadPool(int nThreads)的nThreads就是核心线程数也是最大线程数,队列用的是LinkedBlockingQueue,默认容量Integer.MAX_VALUE,这也是它容易 OOM 的原因,后面避坑章节会细说。
3. 把答案变成能跑通的代码:基础与多线程模块的验证方法
文档里 Java 基础和多线程两个模块的代码片段最多,也最值得动手验证。这一章挑几个典型题,讲清楚怎么从“看懂答案”走到“能自己写出来”。
3.1 hashCode 与 equals 的契约:用“通话”和“重地”验证
文档里有一道很经典的题:两个对象的hashCode()相同,equals()一定为 true 吗?答案是不一定,并给了"通话"和"重地"这两个字符串作为例子。这两个词的 hashCode 相同,但 equals 为 false。你可以直接跑:
public class HashCodeDemo { public static void main(String[] args) { String str1 = "通话"; String str2 = "重地"; // 两个不同字符串的 hashCode 碰撞 System.out.println(String.format("str1:%d | str2:%d", str1.hashCode(), str2.hashCode())); System.out.println(str1.equals(str2)); // false } }输出会是两个相同的哈希值和一个false。这背后的原理是:hashCode 是散列函数,不同输入可能得到相同输出(哈希碰撞),而 equals 才是判断相等性的最终依据。面试时如果只答“hashCode 相同 equals 不一定 true”,面试官可能追问“那为什么重写 equals 必须重写 hashCode”,这时候你要能说出:如果两个对象 equals 相等但 hashCode 不同,它们在 HashMap 里会被放到不同的桶,导致逻辑错误。
3.2 String、StringBuffer、StringBuilder 的性能差异实测
文档第 7 题讲了这三个类的区别,结论是 String 不可变、StringBuffer 线程安全、StringBuilder 非线程安全但性能高。这个结论可以量化验证:
public class StringPerfDemo { public static void main(String[] args) { int loop = 100000; // String 拼接,每次生成新对象 long t1 = System.currentTimeMillis(); String s = ""; for (int i = 0; i < loop; i++) { s += "a"; } long t2 = System.currentTimeMillis(); // StringBuilder 单线程拼接 StringBuilder sb = new StringBuilder(); for (int i = 0; i < loop; i++) { sb.append("a"); } long t3 = System.currentTimeMillis(); // StringBuffer 线程安全拼接 StringBuffer sbf = new StringBuffer(); for (int i = 0; i < loop; i++) { sbf.append("a"); } long t4 = System.currentTimeMillis(); System.out.println("String: " + (t2 - t1) + "ms"); System.out.println("StringBuilder: " + (t3 - t2) + "ms"); System.out.println("StringBuffer: " + (t4 - t3) + "ms"); } }十万次循环下,String 的耗时通常是 StringBuilder 的几十倍甚至上百倍,StringBuffer 因为加了synchronized会比 StringBuilder 略慢。这个实测数据比背结论有说服力得多。参数上,循环次数可以调,但别调到百万级,String 拼接会慢到让你以为程序卡死。
3.3 线程状态与 sleep/wait 的验证
文档第 40、41 题讲了线程状态和sleep()与wait()的区别。sleep不释放锁、wait释放锁,这个区别用代码验证最直观:
public class WaitSleepDemo { public static void main(String[] args) throws Exception { Object lock = new Object(); Thread t1 = new Thread(() -> { synchronized (lock) { System.out.println("t1 拿到锁,准备 wait"); try { lock.wait(); } catch (InterruptedException e) {} System.out.println("t1 被唤醒,重新拿到锁"); } }); Thread t2 = new Thread(() -> { synchronized (lock) { System.out.println("t2 拿到锁,准备 notify"); lock.notify(); System.out.println("t2 调用 notify 后仍持有锁"); } }); t1.start(); Thread.sleep(100); // 确保 t1 先进入 wait t2.start(); } }跑这段代码你会看到:t1 调用wait()后释放了锁,t2 才能进入同步块;t2 调用notify()后并没有立即释放锁,而是等同步块执行完才释放,t1 才被唤醒。如果把wait()换成Thread.sleep(),t1 会一直持有锁,t2 根本进不来。这个对比就是面试官想听到的“释放锁”的实际含义。
4. 避坑与排查:用这份题库复习时最容易翻车的五个点
这份资源本身质量不错,但使用方式不对,很容易出现“背了 208 题还是过不了面试”的情况。下面五条是我见过和踩过的坑。
4.1 只背结论不跑代码,面试追问就露馅
现象:把文档里的答案背得滚瓜烂熟,面试官问“为什么”就答不上来。原因:文档给的是结论,比如“ArrayList 扩容增加 50%”,但没解释为什么是 50% 而不是 100%。解决:每背一个结论,强迫自己补一个“为什么”。ArrayList 扩容 50% 是因为oldCapacity + (oldCapacity >> 1),位运算右移一位相当于除以 2,这样在空间和扩容频率之间取平衡。这类追问在初级岗二面很常见。
4.2 把初级题当高级题背,浪费时间在低频模块
现象:花大量时间背 Zookeeper 的 ZAB 协议、Kafka 的 ISR 机制,结果面试根本没问。原因:没区分岗位级别,初级岗的中间件问题通常停留在“是什么、怎么用”。解决:按第 2 章的分档表分配时间,低频模块知道基本概念即可,把省下的时间用来把 HashMap 的扩容、线程池参数、Spring Bean 生命周期这些高频追问点吃透。
4.3 代码片段直接复制,忽略 JDK 版本差异
现象:文档里的代码在自己机器上跑报错或结果不一致。原因:部分 API 在不同 JDK 版本行为不同,比如String的hashCode算法在极端情况下、HashMap的树化阈值在 JDK 8 前后有变化。解决:跑代码前先确认 JDK 版本,初级面试目前主流是 JDK 8 和 JDK 11,文档里的示例基本兼容,但涉及集合扩容、锁升级的内容要以 JDK 8 为准。用java -version确认,必要时装多个版本切换。
4.4 线程池创建方式记混,参数说不清
现象:面试问“线程池有哪几种创建方式”,答出七种但说不清newFixedThreadPool和newCachedThreadPool的队列区别。原因:文档列了七种方式,但没强调它们底层都是ThreadPoolExecutor的封装。解决:记住核心参数corePoolSize、maximumPoolSize、keepAliveTime、workQueue、threadFactory、handler,然后反推每种创建方式对应的参数组合。比如newFixedThreadPool(n)就是核心=最大=n,队列用无界LinkedBlockingQueue;newCachedThreadPool()就是核心 0、最大Integer.MAX_VALUE、队列用SynchronousQueue、空闲 60 秒回收。
4.5 忽略“对象拷贝”和“反射”模块,结果被问到
现象:觉得反射和对象拷贝是高级内容,跳过不看,结果面试官问“深拷贝和浅拷贝的区别”“反射怎么获取私有方法”。原因:这两个模块在初级岗里确实问得少,但一旦问到就是区分度题。解决:至少掌握浅拷贝(Object.clone()是浅拷贝)、深拷贝(序列化或递归 clone)、反射获取Class对象的三种方式(Class.forName、对象.getClass()、类.class)以及getDeclaredMethod和setAccessible(true)的用法。不用深挖,但要知道。
5. 从 208 题到面试通过:一套可复用的自测与追问技巧
背完题库只是第一步,真正拉开差距的是“自测”和“追问”。我一般会用一个笨办法:把 208 道题当成 208 个面试官,每道题自己先答一遍,录音,然后回放听哪里卡壳。卡壳的地方就是薄弱点,回去补代码验证。
具体操作上,我会按模块做一张自测表,每道题标记三个状态:能流畅答出、能答但磕巴、答不出。只刷“答不出”和“磕巴”的题,流畅的题每周抽一次快速过。这样 208 题的实际复习量会压缩到 60 到 80 题,效率高很多。
追问技巧方面,我总结了一个模板:任何一道题,都问自己三个问题——它是什么、它为什么这样设计、它和相似概念的区别是什么。以HashMap为例:它是什么(基于哈希表的 Map 实现)、为什么这样设计(数组+链表+红黑树兼顾查询和插入效率)、和Hashtable的区别(线程安全、null 支持、扩容方式)。这三个问题答得出来,面试官基本问不倒你。
还有一个容易被忽略的点:文档里的答案有些是“一句话结论”,比如“BIO 是同步阻塞、NIO 是同步非阻塞、AIO 是异步非阻塞”。这种题面试官很可能让你举例说明。我的习惯是给每个概念配一个生活化类比:BIO 像排队打饭,一个人不打完后面的人干等;NIO 像取号机,取完号可以去干别的,叫到号再回来;AIO 像外卖,下单后不用管,送到了通知你。类比不一定严谨,但能帮你在紧张时快速组织语言。
最后说一个我自己的教训。早年我复习面试题,总喜欢从头到尾刷一遍,刷完就扔,结果面试时遇到原题还是答不利索。后来我改成“每道题必须能写出最小验证代码”,哪怕只是三行System.out.println,写出来和看一遍的效果完全不同。从那以后我每次复习任何题库,都强制自己至少跑通其中 30% 的代码示例,剩下的 70% 也要能口述出验证思路。这个习惯帮我省掉了大量“以为自己会了”的幻觉时间。希望帮到你。
本文还有配套的精品资源,点击获取