1. Java 基本數據類型有哪些?
場景:面試官問「String 是不是基本類型?」
答案要點:8 種基本類型:byte, short, int, long, float, double, char, boolean。String 是引用類型。
追問鏈條:
問:為什么需要基本類型而不是全部對象?
答:基本類型更輕量,性能更高。問:對應的包裝類是什么?
答:Byte, Short, Integer, Long, Float, Double, Character, Boolean。問:裝箱/拆箱有性能問題嗎?
答:有,頻繁裝拆箱可能導致額外開銷。
2. == 和 equals 的區別?
場景:字符串比較
"abc" == new String("abc")
。答案要點:
==
比較引用地址;equals
比較內容(String 重寫了 equals)。追問鏈條:
問:為什么 String 要重寫 equals?
答:否則 equals 也只是比較引用。問:哪些類沒重寫 equals?
答:Object 默認 equals 是==
。問:重寫 equals 時為什么要重寫 hashCode?
答:保證在集合(HashMap/HashSet)中一致性。
3. String、StringBuilder、StringBuffer 區別?
場景:面試官問「拼接字符串用哪個效率高」。
答案要點:
String:不可變,每次拼接生成新對象。
StringBuilder:可變,非線程安全,效率高。
StringBuffer:可變,線程安全,效率比 StringBuilder 低。
追問鏈條:
問:為什么 String 設計成不可變?
答:安全(常量池)、緩存、線程安全。問:多線程拼接用哪個?
答:StringBuffer 或用外部同步控制 StringBuilder。問:+ 拼接底層怎么實現?
答:編譯期用 StringBuilder 優化。
4. ArrayList 和 LinkedList 區別?
場景:問「插入/查詢哪個更快」。
答案要點:
ArrayList:基于數組,查詢快 O(1),插入/刪除慢 O(n)。
LinkedList:基于雙向鏈表,插入/刪除快 O(1),查詢慢 O(n)。
追問鏈條:
問:如果在尾部插入呢?
答:ArrayList 均攤 O(1),LinkedList O(1)。問:為什么 LinkedList 查詢慢?
答:需遍歷鏈表節點。問:在實際項目用哪個多?
答:ArrayList 用得更多,因查詢遠多于插入。
5. HashMap 的底層實現?
場景:問「HashMap 為什么快?」
答案要點:JDK1.8 以后,數組 + 鏈表 + 紅黑樹。負載因子默認 0.75,擴容翻倍。
追問鏈條:
問:為什么引入紅黑樹?
答:鏈表過長(>8)時避免 O(n),降到 O(log n)。問:為什么負載因子是 0.75?
答:空間和查找效率的折中。問:線程安全嗎?
答:不安全,線程安全可用 ConcurrentHashMap。
6. ConcurrentHashMap 的原理?
場景:問「多線程下怎么保證安全」。
答案要點:
JDK1.7:分段鎖(Segment)。
JDK1.8:CAS + synchronized 鎖粒度更小。
追問鏈條:
問:為什么不用全局鎖?
答:全局鎖效率低,分段鎖并發度更高。問:JDK1.8 為什么改?
答:優化鎖粒度,減少 Segment 內部沖突。問:適合替代 HashMap 嗎?
答:適合并發場景,但寫性能低于非并發 HashMap。
7. Java 的異常體系?
場景:問「throw 和 throws 區別」。
答案要點:
Throwable → Error(不可處理)、Exception(可處理)。
受檢異常(Checked):必須捕獲或拋出。
非受檢異常(RuntimeException):可不捕獲。
追問鏈條:
問:為什么有 Checked Exception?
答:提醒開發者必須處理異常。問:常見的 RuntimeException?
答:NullPointerException, IndexOutOfBoundsException。問:finally 一定會執行嗎?
答:除非調用 System.exit 或線程中斷。
8. final、finally、finalize 區別?
場景:問「final 和 finally 一樣嗎?」
答案要點:
final:修飾類(不可繼承)、方法(不可重寫)、變量(常量)。
finally:異常處理語句塊。
finalize:Object 方法,GC 前調用(不推薦使用)。
追問鏈條:
問:final 變量必須初始化嗎?
答:是,必須賦值一次。問:finally 能不執行嗎?
答:在 System.exit 時不會。問:finalize 為什么不推薦?
答:不確定性強,性能差。
9. static 關鍵字作用?
場景:問「static 在項目里怎么用?」
答案要點:
修飾變量:類變量,所有對象共享。
修飾方法:類方法,可通過類名直接調用。
修飾代碼塊:類加載時執行一次。
追問鏈條:
問:靜態方法能調用非靜態變量嗎?
答:不能,因為非靜態變量依賴實例。問:static 修飾內部類?
答:靜態內部類不依賴外部類對象。問:項目常見應用?
答:工具類、單例模式。
10. 接口和抽象類的區別?
場景:問「為什么有了抽象類還要接口?」
答案要點:
接口:定義規范(Java8 以后可有默認方法)。
抽象類:定義抽象方法 + 部分實現,支持構造函數。
追問鏈條:
問:能多繼承嗎?
答:接口可多實現,類只能單繼承。問:什么時候用接口?
答:更強調規范,如 DAO 層。問:什么時候用抽象類?
答:有共享邏輯的基類場景。
11. Java 內存模型(JMM)?
場景:問「volatile 的作用」。
答案要點:JMM 規定了多線程間如何通過內存交互,關鍵問題:可見性、有序性、原子性。
追問鏈條:
問:volatile 保證什么?
答:保證可見性和有序性,但不保證原子性。問:如何保證原子性?
答:使用 synchronized 或 Atomic 類。問:指令重排是什么?
答:編譯器/CPU 優化指令順序,可能導致線程安全問題。
12. synchronized 和 ReentrantLock 區別?
場景:問「項目中鎖用哪個多」。
答案要點:
synchronized:Java 關鍵字,JVM 層面實現。
ReentrantLock:Java API,功能更強(公平鎖、可中斷、條件變量)。
追問鏈條:
問:誰性能更高?
答:JDK1.6 后 synchronized 優化,性能差距不大。問:什么時候用 ReentrantLock?
答:需要超時獲取、可中斷、多個條件時。問:什么是可重入?
答:同一線程可重復獲取鎖而不死鎖。
13. JVM 內存結構?
場景:問「對象在哪個區?」
答案要點:
堆(對象、數組)。
方法區(類信息、常量池)。
棧(局部變量)。
程序計數器。
追問鏈條:
問:棧和堆的區別?
答:棧存儲局部變量,生命周期隨線程;堆存儲對象,GC 管理。問:方法區和堆區別?
答:方法區存儲類元信息,堆存儲實例。問:常量池在方法區嗎?
答:JDK8 前在永久代,之后在 Metaspace。
14. 垃圾回收(GC)算法?
場景:問「為什么要分代回收」。
答案要點:
標記-清除。
復制算法(新生代)。
標記-整理(老年代)。
分代收集:針對對象生命周期優化。
追問鏈條:
問:為什么新生代用復制?
答:大部分對象朝生夕死,復制效率高。問:老年代為什么不用復制?
答:對象存活率高,復制代價大。問:常見 GC 器有哪些?
答:Serial, Parallel, CMS, G1, ZGC, Shenandoah。
15. transient 關鍵字作用?
場景:問「對象序列化時怎么忽略某個字段」。
答案要點:
transient
修飾字段,序列化時不會保存。追問鏈條:
問:靜態變量會被序列化嗎?
答:不會,屬于類而非對象。問:如果必須序列化怎么辦?
答:手動實現 writeObject/readObject。問:項目中常見應用?
答:敏感信息(如密碼)、不需要持久化的緩存字段。
16. Java 創建線程的方式有哪些?
場景:問「如果要執行一個并發任務,怎么創建線程?」
答案要點:
繼承 Thread 類。
實現 Runnable 接口。
實現 Callable + FutureTask。
線程池 ExecutorService。
追問鏈條:
問:Runnable 和 Callable 區別?
答:Callable 可返回結果、拋出異常。問:為什么推薦線程池?
答:減少頻繁創建銷毀開銷,統一管理。問:項目里常用哪種?
答:基本都用線程池。
17. sleep() 和 wait() 的區別?
場景:問「線程暫停和釋放鎖有什么不同?」
答案要點:
sleep():線程暫停但不釋放鎖。
wait():暫停并釋放鎖,需配合 synchronized 使用。
追問鏈條:
問:wait 必須在 synchronized 中調用嗎?
答:是,否則拋 IllegalMonitorStateException。問:notify 和 notifyAll 區別?
答:notify 喚醒一個線程,notifyAll 喚醒所有。問:項目中常用嗎?
答:更常用的是并發工具類(如 CountDownLatch、Semaphore)。
18. volatile 能保證原子性嗎?
場景:問「volatile 修飾 i++ 安全嗎?」
答案要點:volatile 只能保證可見性和有序性,不保證復合操作的原子性。
追問鏈條:
問:為什么 i++ 不是原子操作?
答:包含讀取、加一、寫回三個步驟。問:如何保證原子性?
答:使用 synchronized 或 AtomicInteger。問:什么時候只用 volatile?
答:配置刷新、狀態標志。
19. ThreadLocal 的作用?
場景:問「Web 項目里為什么要用 ThreadLocal 保存用戶信息?」
答案要點:為每個線程提供獨立變量副本,避免共享沖突。常用于存儲用戶上下文、數據庫連接。
追問鏈條:
問:ThreadLocal 會造成內存泄漏嗎?
答:可能,若不 remove(),線程池中的線程不會釋放副本。問:它的底層原理?
答:Thread → ThreadLocalMap 存儲。問:怎么避免內存泄漏?
答:用完后及時 remove()。
20. Java IO 和 NIO 區別?
場景:問「為什么高并發用 NIO?」
答案要點:
IO:面向流,阻塞。
NIO:面向緩沖區,非阻塞,支持多路復用。
追問鏈條:
問:NIO 核心組件?
答:Channel、Buffer、Selector。問:NIO 一定比 IO 快嗎?
答:不一定,小規模連接 IO 簡單高效。問:NIO 在哪里用?
答:網絡編程(Netty)。
21. 反射的原理?
場景:問「Spring 是怎么通過反射創建 Bean 的?」
答案要點:反射允許在運行時獲取類信息(Class 對象),動態調用構造器、方法、字段。
追問鏈條:
問:反射的性能如何?
答:比直接調用慢,因需檢查安全、動態查找。問:如何優化反射性能?
答:緩存 Method/Field 對象。問:項目中常見應用?
答:Spring IOC、ORM 框架。
22. 泛型在 Java 中的意義?
場景:問「為什么要有泛型,直接用 Object 不行嗎?」
答案要點:泛型保證類型安全,避免強制類型轉換,提升代碼復用性。
追問鏈條:
問:Java 泛型是如何實現的?
答:編譯期檢查 + 類型擦除。問:為什么叫類型擦除?
答:運行時泛型信息被擦除,只保留 Object 或上限類型。問:能獲取泛型參數的實際類型嗎?
答:不能直接獲取,但可通過反射獲取部分信息。
23. 注解的原理?
場景:問「Spring @Autowired 是怎么生效的?」
答案要點:注解本質是接口,配合反射 + 注解處理器(APT)或運行時解析,實現功能增強。
追問鏈條:
問:注解分幾類?
答:源碼級、編譯期(APT)、運行時。問:注解能繼承嗎?
答:不直接支持,但可用 @Inherited。問:自定義注解怎么實現?
答:定義 @interface + 元注解(@Target, @Retention)。
24. equals 和 hashCode 的關系?
場景:問「為什么要同時重寫 hashCode?」
答案要點:
equals 相等 → hashCode 必須相等。
hashCode 相等 → equals 不一定相等。
追問鏈條:
問:如果只重寫 equals 會怎樣?
答:可能在 HashSet 中重復插入。問:hashCode 默認實現是什么?
答:Object 的 hashCode 基于對象內存地址。問:怎么重寫 hashCode?
答:常用 Objects.hash()。
25. Java 序列化機制?
場景:問「對象如何持久化存儲?」
答案要點:實現 Serializable 接口,使用 ObjectOutputStream / ObjectInputStream。
追問鏈條:
問:為什么要序列化?
答:對象持久化、網絡傳輸。問:serialVersionUID 的作用?
答:保證反序列化版本一致性。問:如果不想序列化某字段?
答:用 transient 修飾。
26. 什么是反序列化漏洞?
場景:安全面試問「為什么要謹慎使用 Java 序列化?」
答案要點:攻擊者可構造惡意字節流,反序列化時觸發惡意代碼。
追問鏈條:
問:如何避免?
答:禁止反序列化不可信數據。問:有替代方案嗎?
答:使用 JSON 序列化。問:Spring Boot 默認用什么?
答:Jackson/JSON。
27. 什么是內部類?有什么類型?
場景:問「成員內部類和靜態內部類的區別?」
答案要點:
成員內部類:依賴外部類實例。
靜態內部類:不依賴外部類實例。
局部內部類:定義在方法里。
匿名內部類:常用于回調。
追問鏈條:
問:內部類能訪問外部類的 private 嗎?
答:能,編譯器會生成橋接代碼。問:為什么用匿名內部類?
答:簡化回調實現(Java8 前)。問:Java8 后更常用什么?
答:Lambda 表達式。
28. 什么是 Lambda 表達式?
場景:問「Java8 為什么引入 Lambda?」
答案要點:Lambda 是匿名函數,簡化函數式編程,常與 Stream API 結合。
追問鏈條:
問:底層原理?
答:通過 invokedynamic 指令實現。問:和匿名內部類區別?
答:語法簡潔,性能更優。問:項目中常用在哪?
答:集合操作、異步回調。
29. 什么是函數式接口?
場景:問「為什么 Lambda 只能用在函數式接口上?」
答案要點:函數式接口 = 只有一個抽象方法的接口(@FunctionalInterface)。
追問鏈條:
問:常見的函數式接口?
答:Runnable, Callable, Comparator, Consumer, Supplier。問:為什么 @FunctionalInterface 可選?
答:只是提醒和編譯期檢查。問:能不能有默認方法?
答:可以,但抽象方法只能有一個。
30. Java Stream API 的作用?
場景:問「為什么要用 Stream,而不是 for 循環?」
答案要點:提供聲明式集合操作(map、filter、reduce),支持并行流,代碼簡潔。
追問鏈條:
問:Stream 是惰性執行的嗎?
答:是,只有遇到終止操作才執行。問:并行流一定比串行流快?
答:不一定,小數據量下反而慢。問:常見場景?
答:數據過濾、聚合統計。
31. JVM 內存結構有哪些?
場景:問「一個 Java 程序運行時,內存是如何劃分的?」
答案要點:
程序計數器
虛擬機棧
本地方法棧
堆
方法區(元空間)
追問鏈條:
問:堆里存什么?
答:對象實例。問:棧里存什么?
答:局部變量、方法調用。問:方法區是什么?
答:存放類信息、常量、靜態變量。
32. 類加載過程是什么?
場景:問「類是怎么被加載到 JVM 的?」
答案要點:加載 → 驗證 → 準備 → 解析 → 初始化。
追問鏈條:
問:初始化階段做了什么?
答:執行靜態變量賦值和靜態代碼塊。問:什么是雙親委派模型?
答:類加載先委托父加載器。問:為什么要有雙親委派?
答:避免重復加載、保證安全。
33. 什么是 GC Roots?
場景:問「垃圾回收時,怎么判斷對象是否存活?」
答案要點:從 GC Roots 出發可達的對象是存活的。
常見 GC Roots:虛擬機棧引用、本地方法棧引用、靜態變量、常量。
追問鏈條:
問:有哪些垃圾回收算法?
答:標記清除、復制、標記整理、分代收集。問:Minor GC 和 Full GC 區別?
答:Minor GC 只收集新生代,Full GC 會收集整個堆和方法區。問:如何避免頻繁 Full GC?
答:減少大對象、優化內存分配。
34. JVM 調優常用參數有哪些?
場景:問「如果線上頻繁 GC 怎么處理?」
答案要點:
-Xms/-Xmx
:堆大小-Xmn
:新生代大小-XX:+PrintGCDetails
:GC 日志
追問鏈條:
問:如何查看 GC 情況?
答:jstat、GC 日志。問:如何定位內存泄漏?
答:jmap dump → MAT 分析。問:堆外內存怎么調優?
答:-XX:MaxDirectMemorySize
。
35. String 為什么是不可變的?
場景:問「為什么 String 修改會生成新對象?」
答案要點:
字符串常量池需要安全性和復用。
線程安全。
緩存 hashCode。
追問鏈條:
問:StringBuilder 和 StringBuffer 區別?
答:StringBuffer 線程安全,StringBuilder 非線程安全。問:為什么推薦用 StringBuilder?
答:性能更好。問:String.intern() 的作用?
答:將字符串加入常量池。
36. equals 和 == 的區別?
場景:問「比較兩個對象是否相等?」
答案要點:
==
比較引用或基本類型值。equals 比較內容,默認繼承 Object 時等同于
==
。
追問鏈條:
問:包裝類用 == 會怎樣?
答:可能比較引用,容易出錯。問:Integer 緩存范圍?
答:-128 ~ 127。問:項目里怎么避免坑?
答:統一用 equals。
37. final 關鍵字的作用?
場景:問「如何定義常量?」
答案要點:
修飾類:不能繼承。
修飾方法:不能重寫。
修飾變量:值不能改變。
追問鏈條:
問:final 修飾對象能變嗎?
答:引用不能變,但對象內容可變。問:final 和 immutability 區別?
答:final 只是引用不可變,immutable 是內容不可變。問:項目中常見用途?
答:工具類、配置常量。
38. static 關鍵字的作用?
場景:問「為什么工具類的方法都是 static?」
答案要點:
修飾變量:類級共享。
修飾方法:無需實例化即可調用。
修飾代碼塊:類加載時執行一次。
追問鏈條:
問:靜態變量存放在哪里?
答:方法區(元空間)。問:靜態方法能訪問非靜態變量嗎?
答:不能,需要實例。問:項目中常見用途?
答:單例、工具類。
39. 接口和抽象類的區別?
場景:問「什么時候用接口,什么時候用抽象類?」
答案要點:
接口:行為規范,多繼承。
抽象類:模板,支持字段、構造方法。
追問鏈條:
問:Java8 接口有什么新特性?
答:默認方法、靜態方法。問:接口能有變量嗎?
答:只能有常量(public static final)。問:設計時如何選擇?
答:抽象類用于繼承體系,接口用于擴展。
40. 什么是多態?
場景:問「為什么要用父類引用指向子類對象?」
答案要點:
編譯看左邊,運行看右邊。
提高擴展性。
追問鏈條:
問:重寫和重載區別?
答:重寫是運行時,多態實現;重載是編譯期。問:多態能提升性能嗎?
答:不是性能,而是設計靈活性。問:項目里常見場景?
答:接口編程、策略模式。
41. 什么是異常體系?
場景:問「為什么要區分 checked 和 unchecked 異常?」
答案要點:
Exception(受檢異常)必須處理。
RuntimeException(非受檢異常)可以不處理。
追問鏈條:
問:常見的受檢異常?
答:IOException, SQLException。問:常見的運行時異常?
答:NullPointerException, IndexOutOfBounds。問:項目里怎么設計異常?
答:統一異常處理(全局異常捕獲)。
42. Java 內存模型 JMM 是什么?
場景:問「為什么多線程需要 synchronized?」
答案要點:JMM 定義了線程如何通過主內存和工作內存交互,保證可見性、有序性、原子性。
追問鏈條:
問:JMM 三大特性?
答:原子性、可見性、有序性。問:volatile 保證了哪些?
答:可見性和有序性,不保證原子性。問:happens-before 原則是什么?
答:JMM 中的內存可見性規則。
43. synchronized 和 ReentrantLock 的區別?
場景:問「為什么有了 synchronized 還要 Lock?」
答案要點:
synchronized:JVM 層實現,語法簡單。
Lock:API 層實現,功能更豐富(可中斷、公平鎖、條件變量)。
追問鏈條:
問:Lock 如何釋放?
答:必須在 finally 里 unlock。問:ReentrantLock 為什么叫可重入?
答:同一線程可以多次獲取鎖。問:項目中什么時候用 Lock?
答:復雜鎖邏輯、需要嘗試鎖時。
44. 什么是 AQS?
場景:問「CountDownLatch 為什么能實現等待?」
答案要點:AQS(AbstractQueuedSynchronizer)基于 CLH 隊列實現,提供獨占/共享鎖語義。
追問鏈條:
問:AQS 支撐了哪些類?
答:ReentrantLock, CountDownLatch, Semaphore。問:AQS 如何實現排隊?
答:通過 state 狀態和隊列。問:項目里用過哪些?
答:常用 CountDownLatch 實現任務并發。
45. Java 的四種引用類型?
場景:問「為什么要有軟引用、弱引用?」
答案要點:
強引用:普通引用,不會回收。
軟引用:內存不足才回收。
弱引用:下次 GC 必回收。
虛引用:無法獲取對象,僅用于跟蹤 GC。
追問鏈條:
問:軟引用常用場景?
答:緩存。問:弱引用常見用法?
答:ThreadLocal Map。問:虛引用呢?
答:管理堆外內存。
46. 什么是類加載器?
場景:問「Java 是怎么找到類的?」
答案要點:
Bootstrap ClassLoader
Extension ClassLoader
Application ClassLoader
自定義 ClassLoader
追問鏈條:
問:如何實現熱加載?
答:自定義 ClassLoader。問:為什么要有自定義類加載器?
答:插件化、熱更新。問:OSGi 是怎么做的?
答:模塊化 ClassLoader。
47. 什么是雙親委派模型?
場景:問「為什么自己寫的 String 類不會被加載?」
答案要點:加載請求先交給父加載器,父加載器無法完成再自己加載。
追問鏈條:
問:為什么要設計雙親委派?
答:保證核心類安全性。問:什么時候會破壞雙親委派?
答:Tomcat、SPI。問:如何打破?
答:自定義 ClassLoader。
48. Java 常用設計模式有哪些?
場景:問「項目里用過哪些設計模式?」
答案要點:單例、工廠、代理、策略、觀察者。
追問鏈條:
問:單例模式如何實現線程安全?
答:雙重檢查鎖、靜態內部類。問:工廠模式有什么好處?
答:解耦對象創建和使用。問:代理模式在 Spring 哪里用到?
答:AOP。
49. 什么是 Optional?
場景:問「Java8 為什么引入 Optional?」
答案要點:避免 NullPointerException,提供函數式 API(map, flatMap, orElse)。
追問鏈條:
問:Optional 是替代 null 嗎?
答:不是,而是輔助工具。問:項目里能用 Optional 作為字段嗎?
答:不推薦。問:Optional 常見用法?
答:返回值包裝。
50. Java17 有哪些新特性?
場景:問「公司升級到 Java17,有哪些不同?」
答案要點:
sealed classes(密封類)
pattern matching for instanceof
switch 表達式增強
JDK 內置 G1/ZGC 優化
追問鏈條:
問:為什么密封類有用?
答:限制繼承,增強可維護性。問:switch 表達式和舊的有啥不同?
答:支持箭頭語法和返回值。問:Java17 適合哪些企業?
答:長期支持 LTS,適合遷移。