:三個月啃下八股文,我是如何贏麻的)
1. 為什么我決定啃下“八股文”這塊硬骨頭六月我接連拿下三個offer薪資漲幅超過預(yù)期。復(fù)盤整個跳槽過程最值錢的居然不是寫在簡歷上的項目經(jīng)歷而是那些被我翻了無數(shù)遍的“八股文”。別急著皺眉我這里的“八股文”不是貶義而是面試圈里對基礎(chǔ)知識的統(tǒng)稱——JVM、并發(fā)、集合、MySQL、Redis、Spring這些問題幾乎每家公司的技術(shù)面都會問到。以前我覺得背這些是“應(yīng)試教育”直到被現(xiàn)實教育過幾次才明白它到底有多重要。去年我面試過幾家中廠簡歷上的項目寫得漂漂亮亮但一到技術(shù)面就露餡。面試官問“ConcurrentHashMap的鎖粒度是怎么優(yōu)化的”我腦子里一片空白項目里用過但沒深究過原理只能支支吾吾。后來想想面試官不是故意刁難你而是通過這些問題快速判斷你的技術(shù)深度。如果你能把這些基礎(chǔ)問題答得清晰透徹至少證明你的計算機(jī)基礎(chǔ)是扎實的遇到線上問題也有能力去排查。所以今年我下定決心花三個月時間系統(tǒng)梳理面試常問的基礎(chǔ)知識也就是大家口中的“背八股文”。很多人對“背八股文”有誤解覺得就是死記硬背。真正背好的人靠的是理解驅(qū)動的記憶把每個知識點當(dāng)成一個故事去講。比如Java內(nèi)存模型不是背“堆、棧、方法區(qū)”七個字而是要能講清楚為什么這樣劃分、對象是怎么創(chuàng)建的、GC為什么存在、不同垃圾收集器適用于什么場景。一旦你理解了背后的設(shè)計邏輯面試時不管怎么被追問都能把知識串起來。這篇文章就是我的實戰(zhàn)復(fù)盤適合正在準(zhǔn)備跳槽的Java開發(fā)、后端工程師以及那些覺得背八股文沒用但又繞不開面試基礎(chǔ)題的人。我會把準(zhǔn)備過程中用到的知識框架、背誦方法、模擬答題的完整思路都整理出來希望能幫你少走彎路。認(rèn)真讀完你會發(fā)現(xiàn)“贏麻了”不是玄學(xué)而是用對方法后的必然結(jié)果。2. 先把“八股文”拆成一張知識地圖2.1 不能一上來就背先定范圍我大概花了五天時間把主流招聘網(wǎng)站的Java后端面試題過了一遍整理出九個高頻模塊Java基礎(chǔ)與集合、JVM、并發(fā)編程、MySQL、Redis、Spring全家桶、操作系統(tǒng)與網(wǎng)絡(luò)、消息隊列、算法與數(shù)據(jù)結(jié)構(gòu)。列出來之后你會發(fā)現(xiàn)這些模塊幾乎就是后端開發(fā)的“地基”。面試官考察你無非是想確認(rèn)兩件事第一你日常開發(fā)中用到的技術(shù)底層原理到底懂多少第二遇到線上問題時你有沒有能力深入排查。地基要是松的上面蓋多少項目都白搭。定了范圍之后還要給每個模塊排出優(yōu)先級。我根據(jù)目標(biāo)公司的崗位要求做了取舍如果崗位偏向業(yè)務(wù)開發(fā)MySQL、Redis、Spring的權(quán)重就很高如果偏向中間件或基礎(chǔ)架構(gòu)那并發(fā)和JVM就要多花時間。我當(dāng)時的目標(biāo)是中大型互聯(lián)網(wǎng)公司的后端崗所以把并發(fā)和JVM放在了最前面。這份優(yōu)先級不是固定的建議你先花半天時間看看招聘JD再用Excel列一張“考點-我的掌握度-優(yōu)先級”的表后面所有計劃都跟著這張表走。2.2 每個模塊背后都在考什么以Java并發(fā)為例面試官絕不是只想聽“synchronized和ReentrantLock的區(qū)別”這種對比。真正有深度的面試官會接著問synchronized鎖升級的過程是怎樣的為什么需要偏向鎖volatile能保證原子性嗎它和原子類的底層實現(xiàn)有什么不同線程池的核心參數(shù)如何設(shè)置如果核心線程被意外終止了怎么辦AQS的設(shè)計思想是什么公平鎖和非公平鎖的區(qū)別體現(xiàn)在哪ThreadLocal的內(nèi)存泄漏問題怎么產(chǎn)生的你看這就是一個追問鏈路。如果你只背了第一層后面四層就扛不住。所以背的時候不能只背“答案”要背“答案背后的設(shè)計意圖”。我習(xí)慣用“三連問”來加深理解這個東西解決了什么問題它為什么這樣設(shè)計如果換個場景還適用嗎例如ReentrantLock對比synchronized它解決了等待不可中斷、非公平導(dǎo)致的線程饑餓、以及多條件喚醒等問題再深入就是AQS的CLH隊列、state狀態(tài)、Condition等待隊列。把這條線理通哪怕面試官換個角度問“Semaphore和CountDownLatch的區(qū)別”你也能從AQS的角度去回答。MySQL模塊也是一樣。索引這塊不能只背“B樹的葉子節(jié)點用鏈表連接”要能回答“為什么MySQL選擇B樹而不是B樹或跳表”“為什么不建議用uuid作為主鍵”“最左前綴原則底層是怎么實現(xiàn)的”。事務(wù)這塊要能解釋ACID怎么靠undo log、redo log、鎖、MVCC來實現(xiàn)而不是只背四個單詞。把這些底層鏈路串起來你已經(jīng)不是“背八股文”了而是在建立自己的技術(shù)知識樹。2.3 給自己劃一個“背到即是賺到”的重點清單我在整理知識地圖的過程中發(fā)現(xiàn)面試官對這幾個問題的出鏡率高得離譜。建議重點背而且一定要背到能脫口而出的程度HashMap的put流程hash擾動函數(shù)、數(shù)組鏈表紅黑樹、擴(kuò)容機(jī)制、為什么負(fù)載因子是0.75ConcurrentHashMap在JDK7和JDK8的區(qū)別分段鎖→CASsynchronized鎖粒度如何變小JVM內(nèi)存區(qū)域和對象創(chuàng)建過程垃圾回收算法CMS和G1的區(qū)別MySQL索引結(jié)構(gòu)、回表、覆蓋索引、索引下推事務(wù)隔離級別、MVCC、間隙鎖Redis的持久化機(jī)制、過期刪除策略、內(nèi)存淘汰策略、緩存穿透/擊穿/雪崩的解決方案Spring的IOC和AOP原理、Bean的生命周期、循環(huán)依賴的解決方式TCP三次握手四次揮手、時間等待狀態(tài)、為什么需要TIME_WAIT線程池的核心參數(shù)、執(zhí)行流程、拒絕策略ThreadLocal的原理和內(nèi)存泄漏問題這張清單覆蓋了絕大多數(shù)技術(shù)面的命門尤其是業(yè)務(wù)后端崗基本不會繞開這些。準(zhǔn)備的時候我建議給每個問題準(zhǔn)備兩個版本一個是三句話以內(nèi)的精簡版用于快問快答另一個是五分鐘以上的完整版用于面試官深挖時的詳細(xì)闡述。平時練習(xí)用完整版面試時先看面試官的態(tài)度他急著問下一個問題就上精簡版他表現(xiàn)出很有興趣再展開講。3. 背了不等于會了“講出來”才是關(guān)鍵3.1 從“輸入”到“輸出”我的三輪背誦法第一輪我是把答案讀熟讀到能復(fù)述大概意思這是最基礎(chǔ)的一層。第二輪我把手機(jī)錄音打開假裝前面坐著一位面試官對著空氣把整個答案講一遍。聽起來有點傻但效果奇好。因為“背下來”和“講出來”是兩碼事講的過程會逼你組織語言理清因果順序。第三輪我對著白板寫提綱只寫關(guān)鍵節(jié)點然后不看提綱重新講一遍。這一輪練的是你在面試現(xiàn)場沒有那么依賴稿子的能力。三輪下來一個普通考點大概需要四到五遍重復(fù)。我每天晚上抽兩個小時早上出門前再花半小時過一遍“易忘點”。時間雖然碎片化但堅持一段時間后面試時的緊張感少了很多因為大腦對這些問題已經(jīng)形成了肌肉記憶。3.2 用“教課”的方式檢驗是不是真懂費曼學(xué)習(xí)法我用了十幾年背八股文也一樣適用。每學(xué)一個知識點我會找出一個完全不懂技術(shù)的人比如我女朋友給她講三分鐘。如果中途講不下去或者她問我“那這個鎖是干嘛的”我答不上來說明我還沒吃透。別怕被嘲笑這種“講不順”的時刻恰恰是進(jìn)步最快的時候。比如講JVM的垃圾回收我第一次講的時候把“可達(dá)性分析”解釋成“從根對象出發(fā)找活對象”她一臉懵地問我“那什么是根對象”我才發(fā)現(xiàn)自己沒想過這個問題。后來我專門去查了GC Roots的幾種類型甚至畫了一張圖才弄明白。帶著這樣的深度去面試遠(yuǎn)比背標(biāo)準(zhǔn)答案讓人信服。如果你身邊沒有合適的聽眾可以把自己講的內(nèi)容錄下來回聽時當(dāng)成一個挑剔的聽眾專門找聽不懂的地方。通常這些地方就是你的知識盲區(qū)。補(bǔ)上盲區(qū)你的八股文才算真正背到位。3.3 實戰(zhàn)模擬一個你一定會遇到的面試問答以最經(jīng)典的“HashMap的put流程”為例我給出我的完整回答框架你可以參考這個框架來組織其他知識點。我的表述是這樣的“當(dāng)調(diào)用put(key, value)的時候首先會通過key的hashCode經(jīng)過擾動函數(shù)算出hash值再用hash和數(shù)組長度減一做一個與運算得到數(shù)組下標(biāo)。如果這個位置是空的就直接放進(jìn)去如果有元素就發(fā)生哈希碰撞。此時會判斷當(dāng)前結(jié)構(gòu)是鏈表還是紅黑樹如果是鏈表就遍歷鏈表用equals方法比較key找到了就替換value沒找到就尾插一個新節(jié)點如果是紅黑樹就按紅黑樹的方式插入。插入完成后判斷鏈表長度是否達(dá)到8并且數(shù)組長度是否達(dá)到64如果鏈表長度達(dá)到8但數(shù)組長度不足64會先擴(kuò)容而不是轉(zhuǎn)紅黑樹。最后判斷當(dāng)前元素數(shù)量是否超過閾值超過就擴(kuò)容。擴(kuò)容不是簡單復(fù)制原數(shù)組而是把每個元素重新計算下標(biāo)再用高低位移動的方式放到新數(shù)組中這也是為什么JDK8的擴(kuò)容性能比JDK7好。”面試官大概率會追問“為什么紅黑樹化的條件需要鏈表長度是8”這時候你要是只背結(jié)論就尷尬了。你要能解釋這是泊松分布下的統(tǒng)計規(guī)律哈希碰撞在隨機(jī)情況下達(dá)到8個的概率極低因此8是一個兼顧時間和空間的折中值。你看這個知識鏈條就從“怎么put”延伸到了“為什么這樣設(shè)計”面試官會覺得你不只是在背題而是真的理解。我建議每個重點問題都準(zhǔn)備兩個延伸點。比如HashMap除了樹化閾值你還可以準(zhǔn)備“l(fā)oadFactor為什么是0.75”和“為什么用尾插法不用頭插法”這些都是高頻追問。把延伸點準(zhǔn)備到位面試時才會真正“贏麻了”。4. 背了忘忘了背這是數(shù)學(xué)題不是玄學(xué)4.1 遵循記憶規(guī)律對抗遺忘曲線八股文內(nèi)容太多背了后面忘了前面很正常。不要焦慮要利用艾賓浩斯遺忘曲線來規(guī)劃復(fù)習(xí)。我的做法是準(zhǔn)備一個表格把知識點按日期排列分別在第一天、第二天、第四天、第七天、第十五天安排復(fù)習(xí)。雖然準(zhǔn)備過程很辛苦但效果極其明顯。到第三周的時候很多第一天背的內(nèi)容已經(jīng)不需要刻意回憶就能脫口而出。具體操作上我沒有買專門的軟件而是用Excel做了個簡陋但實用的復(fù)習(xí)計劃表。每天花十分鐘復(fù)習(xí)當(dāng)天的“復(fù)習(xí)任務(wù)”“復(fù)習(xí)任務(wù)”就是從之前列出的考點里隨機(jī)抽十個出來在紙上寫關(guān)鍵詞然后口述每個關(guān)鍵詞對應(yīng)的知識點。如果有個別點卡殼就在表格里標(biāo)注“待重背”第二天早上必須優(yōu)先補(bǔ)上。這樣循環(huán)下來到面試前一周我已經(jīng)非常確定哪些知識點是自己的短板可以針對性地再突擊一輪。4.2 制造檢索式練習(xí)而不是反復(fù)閱讀很多人復(fù)習(xí)有一個誤區(qū)就是一遍遍地看書、看筆記覺得自己“看熟了就是記住了”。實際上閱讀帶來的熟悉感會欺騙你真正上場的時候大腦一片空白。心理學(xué)家有個概念叫“檢索效應(yīng)”當(dāng)你合上書強(qiáng)迫自己回憶知識點時那種“這個關(guān)鍵詞是什么來著”的卡頓才是最有效的信息鞏固過程。所以整個六月我?guī)缀醪豢础巴暾娲鸢浮敝豢磫栴}列表看到一個問題就要在三秒內(nèi)說出核心思路然后在一分鐘內(nèi)把整個答案講完最后再回頭看筆記。這個過程很虐但收獲巨大。4.3 把八股文揉進(jìn)項目里形成“復(fù)合記憶”純背八股文是能夠應(yīng)付技術(shù)面但遇到項目深挖的環(huán)節(jié)就容易露怯。面試官常在八股文回答后跟一句“你項目里用到了嗎”如果你回答“沒用過”或者“只簡單使用過”面試官會懷疑你的項目真實性。所以我在背每個考點時都強(qiáng)行關(guān)聯(lián)一個項目場景。比如背到Redis的緩存穿透我就對應(yīng)項目里“查詢熱點商品詳情”的場景——如果惡意請求頻繁請求一個不存在的商品ID如何用布隆過濾器攔截再比如背到線程池我會聯(lián)想到項目中的日志異步寫入如何配置核心線程數(shù)和倉庫隊列容量避免高峰時段丟日志。把知識點和項目場景綁定相當(dāng)于一份時間干了雙份活。我還會把項目里實際排查過的問題記下來作為八股文的“實戰(zhàn)佐證”。比如有一次項目內(nèi)存持續(xù)走高我通過jstat觀察Old區(qū)變化發(fā)現(xiàn)大對象不斷進(jìn)入老年代最后調(diào)整了新生代大小后問題緩解。面試時我根本不用刻意背JVM參數(shù)因為這是我真實做過的事情講出來自然有說服力。面試官聽到你能把理論落到實操上這比背任何標(biāo)準(zhǔn)答案都加分。5. 面試現(xiàn)場如何把背過的內(nèi)容講出高級感5.1 提前設(shè)計“起承轉(zhuǎn)合”別讓答案像背書面試最怕的是聽你背課文語調(diào)平平?jīng)]有重點。在模擬練習(xí)的時候我會刻意設(shè)計答案的節(jié)奏先給結(jié)論再講原理最后舉例子。比如面試官問“Spring如何解決循環(huán)依賴”我先說結(jié)論“Spring通過三級緩存來解決單例Bean的循環(huán)依賴。核心思想是提前暴露對象引用把創(chuàng)建過程提前到實例化階段而不是等完整初始化完成后才放入單例池?!比缓笾v原理“第一級緩存放完整的Bean第二級緩存放早期暴露的對象第三級緩存放對象工廠。當(dāng)A依賴B、B依賴A時A在實例化后就把早期引用放入三級緩存B注入A時從三級緩存拿到工廠提前創(chuàng)建A的引用完成注入。最終A和B都完成初始化?!弊詈笤傺a(bǔ)充一句“但它只能解決單例模式下setter注入的循環(huán)依賴構(gòu)造器注入會直接報錯因為構(gòu)造器注入時連早期引用都還沒有。”這樣有結(jié)論、有推導(dǎo)、有邊界面試官一聽就知道你真懂。5.2 如何應(yīng)對“追問的連環(huán)炮”高水平的面試官不會按常理出牌常常把一個知識點問到“你覺得你已經(jīng)不會了”為止。這時候心態(tài)很重要。我的經(jīng)驗是如果你知道就穩(wěn)穩(wěn)地答越答越興奮如果不知道也千萬別直接說“不知道”然后閉嘴而是把你的思考路徑說出來“這個問題我記得是從XXX角度展開的但我對這個細(xì)節(jié)印象不深我猜可能是XXX的原因。不過結(jié)合XXX的原理我可以推理一下……”大多數(shù)面試官更看重你的思維方式而不是那個標(biāo)準(zhǔn)答案。我有一場面試被問到“如果讓你設(shè)計一個分布式鎖你會怎么做”這題沒有標(biāo)準(zhǔn)答案但我從Redis的SETNX講到Redisson的看門狗再從ZK臨時順序節(jié)點引申到實現(xiàn)細(xì)節(jié)最后說“如果要求高可用我會選擇Redis方案配合RedLock做容錯”。這個回答并不是在背八股文而是把平時的積累真正用了起來面試官當(dāng)場點頭。5.3 給自己留一條“知識退路”不會就引到會的領(lǐng)域面試過程中你不可能題題都會但一定不能冷場。我會在準(zhǔn)備階段刻意總結(jié)幾個自己特別熟的知識點比如JVM調(diào)優(yōu)和Redis高可用。一旦被問到不熟悉的領(lǐng)域我會誠實說“這部分我沒深入研究過”然后馬上接一句“不過我了解到的相關(guān)知識是……”順勢把話題引導(dǎo)到自己熟悉的方向。比如面試官問“你看過消息隊列的源碼嗎”我沒看過我就會說“源碼看得不多但我仔細(xì)研究過Kafka的日志存儲結(jié)構(gòu)”然后開始講“稀疏索引、順序?qū)懕P、頁緩存”這些東西。面試官往往不會被你帶跑但至少能感受到你的知識深度和臨場反應(yīng)能力。6. 六月贏麻了的背后是最笨的功夫回頭看看這三個月的經(jīng)歷其實沒有多少捷徑。每天雷打不動兩小時把那些枯燥的知識點拆碎了、揉爛了、再拼起來。我一度也想放棄覺得自己就是個普通開發(fā)背這些有啥用。但真正走到六月當(dāng)我面對面試官拋出的連環(huán)追問能一個個四平八穩(wěn)地拆解時我才明白“贏麻了”三個字背后是踏踏實實的學(xué)習(xí)方法。如果你也在準(zhǔn)備面試我特別想說一句不要迷信“面經(jīng)”也不要迷信“速成”但也不要輕看“背八股文”這件事。關(guān)鍵是用對方法把它當(dāng)成理解技術(shù)的入口而不是背誦的負(fù)擔(dān)。當(dāng)你把每一個問題都理解透能講給別人聽能在項目里找到對應(yīng)場景那面試對你來說就不再是“被考驗”而是“聊天”。我的個人體會是面試官要的不是一個復(fù)讀機(jī)而是一個思維清晰、基礎(chǔ)扎實、能一起解決復(fù)雜問題的人。八股文只是敲門磚而真正讓你贏麻了的是由此建立起來的整個知識體系和表達(dá)自信。