
面試官第一句話往往不是“自我介紹”而是“講講你理解的Java平臺無關性”。很多候選人脫口而出“一次編譯到處運行”然后就沒有然后了。這恰恰暴露了應試者只背了結論、沒有構建知識體系的問題。真正的平臺無關性是一套從源碼到字節碼、再到JVM的完整鏈路.java文件編譯成.class字節碼字節碼不面向任何具體操作系統而是面向JVM規范。不同的操作系統安裝對應的JVM由JVM將字節碼解釋或JIT編譯為本地機器碼。面試官追問“Java為什么慢”如果你能提到HotSpot的JIT會熱點代碼編譯為機器碼而C2編譯器甚至能做激進優化這就說明你理解的是“運行時適應”而非教科書上的死話。繼續深挖基礎String是高頻考點中的釘子戶。別只背“不可變”三個字要理解不可變帶來的安全性和字符串常量池的復用機制。面試官喜歡問String a a b和new String(ab)的區別更進階的是問StringBuilder在循環拼接中的必要性。真正拉開差距的答案是說出“字符串拼接的優化是javac編譯期的常量折疊而循環內的拼接會在字節碼層反復創建StringBuilder”。如果還能補一句“在JDK9之后String內部存儲從char[]換成了byte[]用coder字段區分Latin1和UTF-16”面試官的眼神會不一樣。集合框架HashMap是半個Java面試的縮影HashMap這一關過不了后面對話基本沒法進行。先問自己能默寫出put方法的完整流程嗎從計算hash、定位桶、插入到紅黑樹轉化、擴容每一步都有對應的問題。例如hash函數為什么是(h key.hashCode()) ^ (h 16)因為高16位與低16位異或讓高位信息參與低位運算減少哈希碰撞。為什么加載因子是0.75這是空間和時間成本的折中泊松分布下桶內鏈表長度達到8的概率極低所以閾值定為8。HashMap的最核心思想不是“數組鏈表”而是“根據hash散列盡可能讓數據均勻分布以空間換時間”。如果記不住所有源碼至少要把“為什么樹化閾值是8”和“為什么容量總是2的冪”講清楚這兩個點是判斷是否真讀過源碼的試金石。ConcurrentHashMap是進階版。JDK7的Segment分段鎖與JDK8的CASsynchronized你都應該能畫圖。JDK8拋棄了Segments直接用Node數組put時通過CAS嘗試插入空桶失敗則synchronized鎖住桶頭節點。這里有一個值得思考的點為什么JDK8在并發度上比JDK7更高因為鎖粒度從Segment每段包含多個桶細化為單個鏈表/紅黑樹的頭節點。你還可以引申到擴容時的遷移機制sizeCtl負值表示擴容狀態多線程協助遷移數據。說出“擴容時通過ForwardingNode標記已遷移的桶”就足以證明你研究過并發擴容的實現細節。并發編程從volatile到AQS的思維躍遷并發是Java面試的分水嶺。volatile兩個字背后是JMMJava內存模型、可見性、有序性和內存屏障。面試官愛問“volatile能保證原子性嗎”答案是不能但更精彩的回答是解釋為什么不能i是讀、加、寫三步volatile只保證每次讀都從主內存取最新值但三步中間可能有其他線程穿插。真正理解volatile的人會知道JMM的happens-before規則中有一條“對volatile變量的寫先行發生于之后對它的讀”由此推導出它適用于一寫多讀的狀態標志。如果還能說出“JSR-133規范重新定義了volatile禁止了與普通變量之間的重排序”那就是加分級表現。synchronized是另一個核心。鎖升級過程無鎖→偏向鎖→輕量級鎖→重量級鎖幾乎是必問。但很多候選人只背答案說不清為什么需要偏向鎖因為大多數對象的鎖在生命周期內只被一個線程訪問偏向鎖可以減少無競爭時的CAS開銷。輕量級鎖則是在無競爭情況下用CAS替代互斥。在JDK15之后偏向鎖被廢棄如果你能提到這一點會顯得知識更新及時。并發編程的終極口訣是鎖不是性能問題的根源競爭的頻率才是。所以讀多寫少的場景用ReadWriteLock高并發短操作用synchronizedJVM已經優化得很好了高吞吐場景考慮LongAdder這種分段累加器。AQS幾乎是Java并發包的地基。ReentrantLock、Semaphore、CountDownLatch、ThreadPoolExecutor的Worker都基于AQS。你需要能說出AQS的核心一個volatile int state 一個CLH變體隊列。獲取鎖失敗時線程被封裝成Node入隊通過LockSupport.park掛起釋放鎖時喚醒后繼節點。“公平鎖和非公平鎖的區別在于非公平鎖在獲取時先嘗試一次CAS搶鎖搶不到才排隊。”如果你能畫出AQS的隊列結構并解釋為什么用雙向隊列——因為需要方便地取消等待和喚醒后繼那就已經從使用層面上升到設計層面。線程池是實戰高頻問題。ThreadPoolExecutor的七大參數都知道但面試官真正想聽的是你如何配置。核心線程數怎么定CPU密集型設置為CPU核數1IO密集型通常設置為2 CPU核數但更科學的說法是“線程等待時間與計算時間的比值決定了最佳線程數”。拒絕策略不是隨便選的CallerRunsPolicy用調用者線程執行任務能起到背壓效果AbortPolicy會拋出異常適合及時暴露問題。還要能解釋為什么不能用Executors.newFixedThreadPool因為LinkedBlockingQueue無界任務堆積可能導致OOM。JVM內存與垃圾回收是檢驗真知的標尺JVM的問題逃不掉但很多人只會背幾塊內存區域。要分層理解線程私有程序計數器、虛擬機棧、本地方法棧和線程共享堆、方法區。JDK8把方法區移出變為元空間Metaspace并直接使用本地內存。這一改動的深層原因是永久代經常OOM而元空間默認大小受本地內存限制且字符串常量池和類元數據的存儲分離。“棧管運行堆管存儲”這種話太膚淺更準確的是“棧幀記錄方法調用的狀態堆存對象的實例數據而對象類型信息放在元空間”。垃圾回收算法中復制、標記清除、標記整理要會對比。分代收集理論怎么用新生代用復制對象存活率低老年代用標記-清除或標記-整理對象存活率高。GC Roots有哪些虛擬機棧中引用的對象、靜態屬性引用的對象、常量引用的對象、JNI引用等。要理解“可達性分析”不是掃描所有對象而是從GC Roots出發做圖遍歷因此判斷對象是否可以回收只看它是否被活動對象引用。接下來是垃圾收集器。從Serial到G1到ZGC要能說出適用場景。G1現在仍是主流它把堆分成Region維護可預測的停頓時間模型。G1最革命的設計是“標記清理”可以和用戶線程并發執行并且用Remembered Set來追蹤跨Region引用。如果你想展示深度可以提一下ZGC通過染色指針和讀屏障實現幾乎零停頓突破了幾十TB堆大小的限制。JVM調優問題不要只背參數要結合案例-Xms和-Xmx設為相同值避免擴容抖動-XX:HeapDumpOnOutOfMemoryError導出堆快照然后用MAT分析。調優的本質不是把參數調得花里胡哨而是減少Full GC的頻率和單次時間。類加載機制里“雙親委派”必考。為什么需要雙親委派為了安全——防止自定義類覆蓋java.lang.String。破壞雙親委派的例子Tomcat為了加載不同應用的同名類JDBC的SPI通過Thread.currentThread().getContextClassLoader()加載驅動實現。自己能不能寫一個和String同包名的類不能因為引導類加載器優先加載rt.jar里的類。類加載的“可見性”規則是父加載器看不到子加載器加載的類而子加載器可以看到父加載器加載的類。Spring框架IOC和AOP只是起手式Spring面試題已經從“IOC是什么”變成了“Spring是如何解決循環依賴的”。這個問題幾乎等于送分題但拿滿分的少。標準答案是單例的setter注入循環依賴通過三級緩存解決——一級緩存放成品對象二級緩存放半成品對象提前暴露引用三級緩存放ObjectFactory生成代理對象的工廠。關鍵點在于三級緩存的存在是為了支持AOP代理——如果對象被代理那么其他依賴它的bean拿到的是代理對象而不是原始對象。如果面試官問“構造函數注入的循環依賴能解決嗎”不能因為構造函數執行時對象還沒實例化沒地方提前暴露。AOP的考點則集中在動態代理JDK動態代理要求接口CGLIB通過繼承實現。Spring默認如果目標類有接口就用JDK代理否則用CGLIB。更深刻的理解是AOP的底層不是反射那么簡單而是代理鏈的組裝通過Advisor匹配切點把多個增強器包裝成攔截器鏈遞歸調用時依次執行前置、后置、環繞通知。事務管理也是AOP的實戰應用Transactional失效的幾種情況必須牢記方法內部自調用、private方法、異常被catch、傳播行為設置錯誤等。Spring事務的傳播行為七種中最重要的REQUIRED和REQUIRES_NEW。前者支持當前事務沒有則新建通常默認后者掛起當前事務開啟新事務用于日志記錄或異步操作。事務失效的本質是“事務管理依賴代理而代理只能在通過bean引用調用時生效”。所以自調用this.method()不走代理事務自然就沒效果。如果面試官問“一個大事務里調用了另一個方法想讓這個方法回滾不影響外層”答案就是REQUIRES_NEW開一個新事務讓異常被捕獲后外層按需提交。MySQL索引和事務是數據庫的兩座大山MySQL面試索引模型是首選。InnoDB的BTree為什么好因為樹的高度矮三層就能存千萬級數據葉子節點用雙向鏈表串聯范圍查詢時不需要回溯樹結構。至于為什么不選B-TreeB-Tree的節點存數據導致單節點能存的鍵值少樹變高且范圍查詢需要中序遍歷。BTree的所有數據都在葉子節點非葉子節點只存鍵這樣磁盤I/O次數少且天然支持排序和范圍查詢。還要會看執行計劃explain中的key、rows、typetype從ref到index到all性能遞減。索引失效的常見場景對索引列使用函數、隱式類型轉換、最左前綴不滿足、%開頭的模糊查詢。但最深的坑是“優化器可能因為數據分布而選擇全表掃描即使有索引”。索引不是越多越好每個索引都是一棵BTree寫入時就要維護多棵樹犧牲寫性能。聯合索引的建立原則是“區分度高、查詢頻繁、最左匹配”。如果面試官問“為什么order by慢”可以提到Using filesort如果排序字段正好是聯合索引的后續列可以避免排序。事務隔離級別是必考。四個級別讀未提交、讀已提交、可重復讀、串行化。MySQL默認是可重復讀為什么默認這個因為早期基于binlog的復制在read committed下會出現主從數據不一致不對MySQL在read committed下也可以正常工作。更合理的解釋是MySQL的binlog格式是statement時在read committed下無法正確復制因為READ COMMITTED允許“間隙鎖”釋放而statement日志回放時會出現問題。但到了row格式后可重復讀的默認選擇更多是歷史慣性。MVCC是實現“快照讀”的核心每行記錄隱藏了兩個隱藏列——事務ID和回滾指針通過undo log構建回滾鏈實現不同事務看到不同版本??芍貜妥x在第一次讀時生成快照之后一直讀這個快照讀已提交是每次讀都生成新快照。至于當前讀需要用lock in share mode或for update走的是鎖機制不是MVCC。鎖問題表鎖、行鎖、間隙鎖、臨鍵鎖。Next-Key Lock是間隙鎖和記錄鎖的結合前開后閉區間用于解決幻讀。在可重復讀級別下MySQL通過Next-Key Lock鎖住查詢范圍防止其他事務插入新記錄從而保證當前讀的一致性。如果范圍查詢的記錄不存在會退化為純間隙鎖。還有死鎖的排查show engine innodb status查看最近死鎖信息分析兩個事務持有哪些鎖、等待哪些鎖通常通過調整事務順序或縮小鎖范圍解決。Redis緩存三兄弟和分布式鎖Redis在面試中幾乎和Java一樣重要。首先是緩存穿透、擊穿、雪崩三兄弟。穿透請求的數據在緩存和數據源中都不存在用“布隆過濾器”或“緩存空對象”解決。擊穿某個熱點key過期瞬間大量請求打到DB用“互斥鎖”或“熱點key永不過期邏輯過期”解決。雪崩多個key同時過期或Redis宕機用“過期時間加隨機值”、“集群高可用”、“限流降級”解決。這三件事拷問的不是你記住了多少方案而是你是否理解“緩存是擋在DB前面的第一道防線防不住就要保護DB”。Redis持久化RDB和AOF。RDB是某一時刻的內存快照適合備份和重啟加載AOF記錄寫命令文件大、恢復慢但安全性高?;旌铣志没疉OF重寫時把RDB內容作為AOF文件開頭這樣加載快且丟失最少。面試官喜歡問“RDB持久化時還能處理寫命令嗎”答案是能的因為Redis通過fork子進程生成快照父進程繼續處理命令寫時復制COW保證數據一致性。如果還能提到appendfsync的everysec是默認配置每秒刷盤最多丟一秒數據就很完善了。分布式鎖是應用層高頻題。簡單的setnx命令容易踩坑要設置過期時間要用set key value nx ex 3000原子命令。釋放鎖時要先驗證value是否是自己設的用Lua腳本保證“檢查刪除”的原子性。更健壯的方案是Redisson的看門狗機制自動續期。真正的資深回答會指出Redis分布式鎖不是銀彈在主從切換下可能丟失鎖如果必須強一致要用Redlock或ZooKeeper鎖。但Redlock本身也有爭議馬丁·克萊普曼專門批判過它。能說出這個爭議說明你讀過分布式系統的經典討論。面試不是背誦而是用體系串聯碎片把上面這些知識點串起來看Java面試其實考察的是“三條線”第一條線是語言基礎從集合到并發到JVM反映你寫代碼時知不知道底層在發生什么第二條線是框架從Spring到數據庫到緩存反映你構建系統時能不能理清組件之間的協作第三條線是“為什么”每個高頻問題背后都有設計權衡比如HashMap為什么用紅黑樹而不是AVL樹因為紅黑樹插入刪除的旋轉次數更少。Spring為什么用三級緩存因為一級放不下代理對象二級解決不了循環引用。MySQL為什么默認可重復讀因為早期主從復制要保證一致性。面試官想看到的不是你背誦的答案而是你在腦海里構建的一張知識圖譜每個節點都有連接。所以準備面試時不要一條一條背八股文。試著從一道題切入比如“HashMap為什么線程不安全”你會引出數據覆蓋、死循環、ConcurrentHashMap然后到CAS到鎖到AQS到線程池到JUC工具類再到Spring的單例池循環依賴……一路追問下去你會把所有高頻考點串成一張網。把知識織成網而不是堆成山這才是應對Java面試的最優策略。祝你拿下心儀offer。