首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 核心面試題精選 36 題:從基礎語法到 JVM 底層原理

     編輯:Java 綜合教學

前言

本文匯總 36 道高頻 Java 核心面試題,涵蓋基礎語法、集合框架、併發編程、JVM 內存模型、類加載機制、GC 算法與調優、數據庫、設計模式等全棧知識體系。每題給出標準答案、核心考點、易錯陷阱及實戰建議,適合校招、社招系統性複習與查漏補缺。


第一部分:Java 基礎與集合框架 (1-15)

1. String、StringBuilder、StringBuffer 三者區別與選型

答案:

  • String:不可變對象,每次修改產生新對象,適用於少量、只讀字符串操作
  • StringBuilder:可變、非線程安全、效率高,單線程大量拼接首選
  • StringBuffer:可變、線程安全(方法級 synchronized)、效率較低,多線程環境使用

底層原理:三者最終都操作 char[](Java 9+ 為 byte[] + coder),擴容機制為 1.5 倍 + 1。拼接優化:編譯器將 + 優化為 StringBuilder.append(),但循環中拼接仍會重複創建 Builder,需手動提取。

2. Collection vs Collections、集合體系結構

核心區別:

  • Collection:頂層接口,繼承 Iterable,子接口 List(有序、可重複)、Set(無序、唯一)、Queue(隊列)
  • Collections:工具類,全 static 方法:排序、二分查找、線程安全包裝、空集合、最大最小值等

注意:Map 不繼承 Collection,屬於獨立體系(鍵值對)。

3. 哪些集合是線程安全的?

集合實現方式備註
Vector方法級 synchronized已過時,效率低,推薦 Collections.synchronizedList(new ArrayList<>()) 或 CopyOnWriteArrayList
Stack繼承 VectorLIFO,同 Vector 缺點,推薦 Deque (ArrayDeque)
Hashtable方法級 synchronized已過時,Key/Value 不可為 null,推薦 ConcurrentHashMap
Enumeration迭代器前身已過時,推薦 Iterator
ConcurrentHashMapCAS + synchronized (JDK8) / 分段鎖 (JDK7)高併發首選,Key/Value 均不可為 null
CopyOnWriteArrayList/Set寫時復制讀多寫少場景,迭代器弱一致性

4. 線程安全的含義與實現手段

定義:多線程訪問同一對象時,無需調用方同步,對象行為正確(符合規範)。

實現層級:

  • 不可變對象:final 字段、無 setter、深拷貝(String、Integer、自定義不可變類)
  • 線程封閉:ThreadLocal、棧封閉、Ad-hoc 封閉
  • 同步機制:synchronized、Lock、volatile、原子類、並發容器

5. 八大基本類型大小與包裝類

見前文「Java 基礎核心知識體系總結」表格。為什麼需要包裝類:泛型參數只能是引用類型、集合存儲、反射調用、可為 null 表示「無值」語義。

6. Switch 支持 String 的版本與原理

  • JDK 7+ 支持 String、enum、byte/short/char/int 包裝類
  • 原理:編譯器基於 hashCode() + equals() 降級為 int switch,再通過 tableswitch/lookupswitch 指令實現

7. == vs equals()

維度==equals()
類型運算符Object 方法
基本類型比較值不適用
引用類型比較地址(引用)默認比地址,常重寫比內容
null 安全安全需非空判斷或 Objects.equals()

8. 四種引用類型與應用場景

詳見前文 GC 章節。面試加分點:WeakHashMap 鍵用弱引用,ThreadLocalMap Entry 繼承 WeakReference,Guava Cache 用軟引用實現內存敏感緩存。

9. hashCode() 作用與契約

  • 作用:哈希容器(HashMap/HashSet)確定桶位置,提升查找效率
  • 契約:equals 相等 → hashCode 必相等;hashCode 相等 → equals 不一定相等
  • 最佳實踐:用 31 為質數組合字段,Objects.hash(field1, field2...) 或 IDE 生成

10. ArrayList vs LinkedList vs Vector

特性ArrayListLinkedListVector
底層結構動態數組雙向鏈表動態數組
隨機訪問O(1) 快O(n) 慢O(1)
頭尾增刪O(n) 需移動O(1) 快O(n)
線程安全否否是 (synchronized)
擴容機制1.5倍無2倍

選型:默認 ArrayList;頻繁頭尾操作用 LinkedList(或 Deque);多線程用 CopyOnWriteArrayList 或同步包裝。

11. Map、Set、List、Queue、Stack 特點

  • Set:無序、唯一(HashSet/LinkedHashSet/TreeSet)
  • List:有序、可重複、索引訪問
  • Queue:FIFO(LinkedList、ArrayDeque、PriorityQueue)
  • Stack:LIFO,繼承 Vector,不推薦,用 Deque
  • Map:鍵值映射、Key 唯一

12. HashMap vs Hashtable

維度HashMapHashtable
線程安全否是 (synchronized)
Null 支持Key/Value 可為 null均不可為 null
繼承體系AbstractMap → MapDictionary → Map
初始容量/擴容16 / 2倍11 / 2n+1
遍歷方式Iterator (fail-fast)Enumerator + Iterator
JDK8 優化鏈表轉紅黑樹 (threshold=8)無

13. HashMap vs LinkedHashMap vs TreeMap

  • HashMap:無序,O(1) 訪問,通用首選
  • LinkedHashMap:保持插入序/訪問序,雙向鏈表維護順序,LRU 緩存實現基石
  • TreeMap:紅黑樹,Key 自然序/自定義 Comparator 排序,O(log n),有序遍歷、範圍查詢首選

14. try-catch-finally 中有 return,finally 執行嗎?

結論:一定執行(JVM 保證),除非 System.exit()、棧溢出、死循環、殺進程。

返回值規則:

  1. try/catch 有 return → 先計算返回值保存到局部變量表 → 執行 finally → 返回保存值
  2. finally 中修改返回值(基本類型無效,引用類型可修改對象內容)
  3. finally 中有 return → 覆蓋 try/catch 返回值,極其不推薦

15. OOM 常見類型與排查

異常類型原因排查工具/參數
Java heap space堆內存不足/洩漏-Xmx/-Xms, jmap -histo:live, MAT 分析 heap dump
PermGen space (JDK7)類加載過多/字符串常量池-XX:PermSize/MaxPermSize, -XX:+UseCompressedClassPointers
Metaspace (JDK8+)類元數據過多-XX:MaxMetaspaceSize, jcmd GC.class_histogram
StackOverflowError遞歸過深/死循環-Xss 設置棧大小, jstack 定位調用鏈
Direct buffer memoryNIO 直接內存/Netty 泄漏-XX:MaxDirectMemorySize, 監控 DirectBuffer 計數

第二部分:面向對象與併發編程 (16-28)

16. 面向對象三大特征

  • 封裝:隱藏內部細節,暴露公共接口,private 字段 + 公共方法
  • 繼承:子類複用父類非 private 成員,單繼承,super 調用父構造
  • 多態:同一引用指向不同實例,調用同一方法表現不同行為(重寫實現)

17. Override vs Overload

維度Override (重寫)Overload (重載)
關系父子類同一類內
方法簽名完全相同名稱相同,參數列表不同
返回類型兼容 (协变返回类型)可不同
拋出異常子類 ≤ 父類無限制
多態類型運行時 (動態分派)編譯時 (靜態分派)

18. Interface vs Abstract Class

維度InterfaceAbstract Class
繼承多實現單繼承
方法JDK8 前全 abstract,JDK8+ default/static/private可有實現方法
字段隱式 public static final任意修飾符
構造方法無有 (子類調用)
設計意圖契約/能力定義代碼復用/模板方法

19. 靜態內部類 vs 非靜態內部類

  • 靜態內部類:無外部類引用,可獨立創建,只能訪問外部靜態成員
  • 非靜態內部類:隱持外部類引用 (OuterClass.this),需外部實例創建,可訪問所有成員
  • 應用:Builder 模式、單例模式(靜態內部類延遲初始化)、Handler 防洩漏(靜態+弱引用)

20. 多態實現原理

  • 靜態分派 (重載):編譯期根據靜態類型決定,invokevirtual 不參與
  • 動態分派 (重寫):運行期根據實際類型,通過虛方法表 (vtable) 查找實際方法,invokevirtual 指令核心邏輯

21. 實現多線程兩種方式:Thread vs Runnable

推薦 Runnable/Callable:

  • 避免單繼承限制
  • 任務與執行分離,支持線程池復用
  • 同一任務實例可被多線程共享(賣票案例)

22. 線程同步機制:synchronized vs Lock vs ReentrantLock

特性synchronizedReentrantLock
類型關鍵字 (JVM 內置)類 (AQS 實現)
鎖釋放自動 (塊結束/異常)手動 unlock() (finally 中)
可中斷否lockInterruptibly()
公平鎖非公平可選 (構造傳 true)
條件變量wait/notify (單一)多 Condition (newCondition())
性能 (低爭用)優化後極佳 (偏向鎖/輕量級鎖)略低
性能 (高爭用)膨脹為重量級鎖穩定優於 synchronized

23. Java 鎖等級:方法鎖、對象鎖、類鎖

  • 方法鎖:synchronized void method() → 鎖 this
  • 對象鎖:synchronized(this) / synchronized(obj) → 鎖指定對象
  • 類鎖:static synchronized void method() / synchronized(MyClass.class) → 鎖 Class 對象

24. 生產者消費者模式:阻塞隊列實現

// 阻塞隊列版 (推薦)
BlockingQueue queue = new LinkedBlockingQueue<>(100);

// 生產者
void produce(Task t) throws InterruptedException {
    queue.put(t); // 滿則阻塞
}

// 消費者
Task consume() throws InterruptedException {
    return queue.take(); // 空則阻塞
}

核心優勢:無需手寫 wait/notify,內置阻塞語義,ArrayBlockingQueue (有界)、LinkedBlockingQueue (可選界)、SynchronousQueue (直接傳遞)。

25. ThreadLocal 設計理念與內存洩漏防範

  • 本質:ThreadLocalMap 存儲在 Thread 內,Key 為 WeakReference,Value 為任意對象
  • 作用:線程隔離變量副本,避免參數層層傳遞(如請求上下文、數據庫連接、Session)
  • 洩漏風險:Thread 長生命週期 + ThreadLocal 不 remove → Key 為 null 但 Value 強引用 → 內存洩漏
  • 最佳實踐:try { ... } finally { threadLocal.remove(); },線程池場景必須清理

26. 線程池七大參數與四種常用類型

new ThreadPoolExecutor(
    corePoolSize,           // 核心線程數
    maximumPoolSize,        // 最大線程數
    keepAliveTime, unit,    // 非核心線程存活時間
    workQueue,              // 任務隊列 (有界/無界)
    threadFactory,          // 線程工廠 (命名、優先級、守護)
    handler                 // 拒絕策略
);
類型核心/最大隊列適用場景
FixedThreadPooln / nLinkedBlockingQueue (無界)長期固定負載,慎用 (OOM 風險)
CachedThreadPool0 / Integer.MAXSynchronousQueue (直傳)短任務突發,慎用 (線程爆炸)
SingleThreadExecutor1 / 1LinkedBlockingQueue串行任務,保證順序
ScheduledThreadPoolcore / 無界DelayedWorkQueue定時/周期任務

生產建議:自定義有界隊列 + 拒絕策略,監控隊列積壓、線程數、拒絕數指標。

27. wait() vs sleep()

維度wait()sleep()
所屬類ObjectThread
鎖狀態釋放鎖持有鎖
喚醒方式notify/notifyAll / 中斷 / 超時時間到 / 中斷
調用前提必須持有對象鎖 (同步塊內)無
語義線程通信/等待條件單純延時

28. foreach vs for 循環效率

  • 數組:foreach 編譯為普通 for,性能一致
  • 集合:foreach 編譯為 Iterator,比下標遍歷 (ArrayList) 慢 ~2 倍,但代碼更簡潔安全
  • 建議:ArrayList 熱點路徑用下標 for,其他場景優先 foreach 可讀性

第三部分:JVM 進階:內存模型、GC、類加載 (29-36)

29. Java IO vs NIO

維度BIO (IO)NIO
核心抽象Stream (流)Channel + Buffer + Selector
讀寫方式阻塞非阻塞/異步
線程模型一連接一線程一線程多連接 (Reactor)
數據處理順序讀寫,無緩衝區控制緩衝區隨機讀寫,可前後移動
適用場景連接數少、低併發高併發、長連接 (Netty 基石)

30. 反射機制:原理、用途、性能優化

  • 原理:運行時獲取類元數據 (Class 對象),動態創建實例、訪問字段、調用方法
  • 核心類:Class、Constructor、Field、Method
  • 用途:框架核心 (Spring IOC/AOP、MyBatis、Hibernate)、序列化、動態代理、注解處理器
  • 性能:比直接調用慢 10-100 倍,優化:setAccessible(true)、緩存 Method/Field 對象、MethodHandle/VarHandle (JDK9+)、GraalVM 內聯

31. 泛型核心:擦除、通配符、PECS 原則

  • 類型擦除:編譯期泛型信息擦除為原始類型,運行期無泛型 (List → List)
  • 通配符:? extends T (上界/生產者/只讀)、? super T (下界/消費者/只寫)
  • PECS:Producer Extends, Consumer Super
  • 不可變性:List 不能轉為 List,但可用 List<? extends Object>

    32. XML 解析:DOM vs SAX vs StAX vs DOM4J

    解析器模式內存速度特點
    DOM樹模型高 (全載入)慢可修改、隨機訪問、小文件首選
    SAX事件流低 (流式)快只讀、線性、大文件、回調式
    StAX拉取流低快可控制解析進程、雙向游標
    DOM4J/JDOM樹模型中中API 友好、XPath 支持、實際開發常用

    33. 設計模式:必懂 7 大核心模式

    1. 單例:枚舉/靜態內部類/雙重檢查鎖 (DCL+volatile)
    2. 工廠:簡單工廠/工廠方法/抽象工廠,解耦創建邏輯
    3. 適配器:類適配器(繼承)/對象適配器(組合),接口不兼容轉換
    4. 責任鏈:Handler 鏈,請求傳遞 (Filter、Interceptor、Netty Pipeline)
    5. 觀察者:發布-訂閱,解耦 (EventListener、Guava EventBus、Spring Event)
    6. 策略:算法族封裝,運行時切換 (Comparator、支付策略)
    7. 模板方法:骨架在父類,細節延遲到子類

    34. JNI 使用場景與開發流程

    • 場景:高性能計算、硬件驅動調用、大內存需求、系統級服務、現有 C/C++ 庫復用
    • 流程:聲明 native 方法 → javah 生成 .h → 實現 .c/.cpp → 編譯為 .so/.dll → System.loadLibrary 加載
    • 代價:喪失跨平台性,各 OS/架構需單獨編譯,調試極難,JNI 調用開銷大 (不適合高頻細粒度調用)

    35. MySQL 存儲引擎:InnoDB vs MyISAM

    特性InnoDBMyISAM
    事務支持 (ACID)不支持
    鎖粒度行級鎖表級鎖
    外鍵支持不支持
    全文索引5.6+ 支持支持
    count(*)掃表/統計估算直接讀元數據 (極快)
    崩潰恢復Redo/Undo Log無,易損壞
    適用場景高併發寫、事務、可靠性要求高讀多寫少、全文搜索、非關鍵數據

    36. Servlet 生命週期與線程安全

    • 生命週期:實例化 → init() (一次) → service() (每請求一線程) → destroy() (一次)
    • 線程安全:非線程安全,單例多線程模型
    • 解決方案:
      1. 不定義實例變量 (無狀態)
      2. 局部變量/參數傳遞
      3. ThreadLocal 隔離
      4. 同步塊 (性能差)
      5. 無狀態 Service Bean (Spring 模式)

    第四部分:JVM 核心原理與調優 (37-46 合併為綜合題)

    JVM 內存模型 (JDK 8+)

    ┌─────────────────────────────────────┐
    │              堆                       │
    │  ┌─────────────┐ ┌─────────────────┐ │
    │  │   新生代     │ │      老年代      │ │
    │  │ Eden + S0/S1 │ │                 │ │
    │  └─────────────┘ └─────────────────┘ │
    ├─────────────────────────────────────┤
    │            元空間                      │  (類元數據、常量池、靜態變量、JIT 代碼)
    ├─────────────────────────────────────┤
    │  虛擬機棧 / 本地方法棧 / 程序計數器      │  (線程私有)
    ├─────────────────────────────────────┤
    │            直接內存                    │  (NIO、Netty、DirectByteBuffer)
    └─────────────────────────────────────┘
    

    對象創建 → 內存分配 → 訪問定位

    1. 類加載檢查 → 分配內存 (指針碰撞/空閒列表) → 初始化零值 → 設置對象頭 (HashCode、GC 信息、鎖狀態) → 執行 <init>
    2. TLAB (Thread Local Allocation Buffer):每線程預分配伊甸區塊,無鎖分配,提升吞吐
    3. 訪問定位:句柄池 (穩定) vs 直接指針 (HotSpot 默認,速度快)

    GC 判定算法

    • 引用計數:簡單、循環引用無法解決 (Python 用),Java 不採用
    • 可達性分析 (根搜索):GC Roots → 引用鏈 → 可達=存活,不可達=可回收
    • GC Roots:棧局部變量、靜態字段、常量、JNI 引用、同步鎖持有對象、類對象等

    三大收集算法

    算法原理優點缺點適用區
    標記-清除標記存活 → 清除死亡基礎算法碎片多、效率低老年代 (配合整理)
    標記-整理標記 → 移動存活對象一端 → 清理無碎片移動開銷大、需更新引用老年代
    複製劃分兩塊,用一塊,存活複製到另一塊無碎片、分配快內存利用率 50%新生代 (Eden+S0/S1)

    優化思路:分代收集 (新生代複製 + 老年代標記整理/並發標記)、大對象直接進老年代、長期存活對象晋升、動態年齡判斷。

    核心收集器對比

    收集器類型區域算法特點組合
    Serial單線程新/老複製/整理簡單、Client 模式Serial Old
    ParNew多線程新複製Serial 多線程版,配合 CMSCMS
    Parallel Scavenge多線程新複製吞吐量優先、自適應調節Parallel Old
    CMS並發老標記-清除低停頓、CPU 敏感、浮動垃圾、碎片ParNew
    G1並發+並行全堆標記-整理+複製分 Region、可預測停頓、混合收集獨立
    ZGC/Shenandoah並發全堆著色指針/轉發指針亞毫秒停頓、大堆 (TB 級)獨立

    Minor GC / Major GC / Full GC 觸發條件

    • Minor GC:Eden 區滿 → 引發,頻繁、快
    • Major GC:老年代滿 / 老年代分配不足 / 晋升失敗 / 手動 System.gc()
    • Full GC:Major GC + 全堆掃描 (CMS 除外並發),極其昂貴
    • 觸發 Full GC 場景:老年代空間不足、元空間不足、System.gc()、Heap Dump、CMS 併發失敗退化為 Serial Old

    常用調試工具

    工具功能核心參數
    jps列出 Java 進程-l (主類), -v (JVM 參數)
    jstatGC/類加載/編譯統計-gcutil -gc -gccapacity -gcnew -gcold 間隔 次數
    jmap堆快照/直方圖-heap -histo:live -dump:live,format=b,file=heap.hprof
    jstack線程快照-l (鎖信息), -m (混合模式)
    jconsole / jvisualvm圖形化監控遠程連接、MAT 插件分析 dump
    Arthas線上診斷神器dashboard, trace, watch, tt, heapdump

    類加載五階段與雙親委派模型

    1. 加載:獲取字節流 → 轉換數據結構 → 生成 Class 對象
    2. 驗證:文件格式、元數據、字節碼、符號引用驗證
    3. 準備:靜態字段分配內存、賦默認值 (final 直接賦值)
    4. 解析:符號引用 → 直接引用 (類/接口/字段/方法)
    5. 初始化:執行 <clinit> (靜態塊/靜態字段賦值),觸發條件:new/getstatic/putstatic/invokestart/reflect/主類/子類初始化/接口默認方法

    雙親委派:

    Bootstrap (啟動) → Extension (擴展) → Application (應用) → Custom (自定義)
         rt.jar              lib/ext                 classpath              用戶定義
    
    • 流程:子先委派父加載,父無法加載 (ClassNotFoundException) 子再嘗試
    • 破壞雙親委派:JNDI/SPI (線程上下文類加載器)、熱部署 (OSGi/模塊化)、Tomcat (WebappClassLoader 先自載)

    方法分派:靜態 vs 動態

    • 靜態分派 (重載):編譯期根據靜態類型決定,invokestatic/invokevirtual (參數類型)
    • 動態分派 (重寫):運行期根據實際類型決定,invokevirtual 查虛方法表
    • 單分派:Java 基於接收者類型分派 (靜態多參數、動態單接收者)

    總結與複習建議

    1. 分層複習:語法基礎 → 集合併發 → JVM 原理 → 中間件/架構
    2. 畫圖講解:內存模型、GC 算法、類加載、鎖升級、線程池流程
    3. 手寫核心代碼:單例 (DCL)、生產者消費者、線程池、LRU、快排、二分
    4. 實戰排查:準備 3-5 個 OOM/死鎖/CPU 100%/RT 飆升案例,講清定位步驟
    5. 源碼閱讀:HashMap、ConcurrentHashMap、ThreadPoolExecutor、ArrayList、ReentrantLock、ClassLoader

    Java 體系龐大,但核心原理貫通。掌握「為什麼這樣設計、邊界在哪、如何權衡」,面試與架構設計才能游刃有餘。祝各位 Offer 滿懷!

    1. 上一篇文章:
    2. 下一篇文章:
    Copyright © 程式碼之家 All Rights Reserved