複習一週,京東+百度一面,不小心都拿了Offer

你知道的越多,你不知道的越多

點贊再看,養成習慣

本文 GitHub https://github.com/JavaFamily 已收錄,有一線大廠面試點思維導圖,也整理了很多我的文檔,歡迎Star和完善,大家面試可以參照考點複習,希望我們一起有點東西。

前言

還記得我上週說的重慶郵電研二的讀者麼?

、

知道他拿了Offer之後我也很開心,我就想把它的面試經歷和麪試題分享出來,這樣讓大家也知道,目前實習的面試大概是怎麼樣子的。

很優秀的是,他每次面試都會自己做總結,我一說他就直接給到了我文檔,優秀的仔,我可能知道他拿這麼多Offer的原因了吧。

整個文章我本來想貼一下面試過程,然後貼下面試題就完事了,這就可以水一篇了。

但是你們肯定會罵我是渣男,不負責任,題目都給了還不給我答案。

我暖男來的,最後還是給你們寫了答案,別愛我,沒結果。

正文

這是一個秋高氣爽的早上,Angela丙丙一如既往的睡着懶覺,呼、呼、呼的呼嚕聲忽弱忽強。

叮叮叮,⏰鬧鐘響了,Angela丙丙如往常般,閉着眼睛摸到了手機並關了,正準備呼呼大睡。

突然一個激靈,Angela丙丙想到了什麼,“今天還有百度和京東的面試,臥*差點睡過頭”。

一眨眼的功夫,Angela丙丙已經坐在了,京東和百度一面的會議室了。(我整合了兩個公司一面的問題,做了去重)

門開了,一個大腹便便,穿着格子襯衣的中年男子,拿着一個滿是劃痕的mac向你走來,看着快禿頂的頭髮,心想着肯定是尼瑪頂級架構師吧!

是不是很熟悉,是我第一篇文章的面試官
是不是很熟悉,是我第一篇文章的面試官

小夥子自我介紹下,順便把你做過的項目介紹一下。

面試官您好我叫Angela丙丙今年23歲,我來自貴州遵義,畢業於藍翔技工職業學校電子信息工程專業,今年已經工作兩年了,之前有過華爲的實習經歷,主要在nova團隊負責核心組件開發,和團隊小夥伴攻堅難題。

現在的公司是天貓國際,主要負責活動小組的研發,擔任小組的小組長,在公司一年期間,參加過大大小小活動30多場,活動爲公司帶來9次過千萬的GMV的活動,負責的項目主要是使用了微服務的設計思想和分佈式的部署方式。

經常用到的中間件有Redis,RocketMq,ElasticSearch,Logstash,Canal,、Dubbo等等,對Dubbo的源碼有着深入的研究和見解,並且仿照Dubbo用Netty也寫了一個類Rpc框架,現在已經開源到了GitHub,並且有了2萬的Star。

並且會用業餘時間撰寫技術博客,在各大博客論壇都要 一定的粉絲量。

我對電商所有的業務流程都有一些瞭解,對線上問題處理以及性能調優都有自己的理解,對業務的研發設計流程也十分熟悉。

因爲個人原因選擇了離職,如果能有幸加入貴公司一起共事,我想我能第一時間上手併產出結果,以上就是我的自我介紹,謝謝面試官。

Tip以上自我介紹全部內容都是虛構的,只是給大家一個面試自我介紹的Demo,面試題就是Angela小丙的面試題了。

Angela丙丙?簡歷上看到你做過項目組長,你在做組長之後做了那些工作,說一下你學到了那些東西。

帥氣的面試官您好,進入公司之後,我因爲項目調動成爲了小組的Leader,這並沒有我想的爽,因爲之前就是負責自己分內的事情就好了,現在需要有項目的大局觀,還有項目進度把控,已經組員的代碼和方案的Review。

小組同學都非常優秀,我如果要Hold住他們,就得逼迫自己去不斷的學習,所以這段時光非常痛苦,但是得到的收穫卻是實實在在的。

有看過HashMap源碼嗎?

嗯嗯這個我看過的,因爲實際開發過程中,Map還是比較常見的面試題。

JDK1.7中HashMap的put()方法全過程。

JDK1.8有那些變化。

JDK1.7當中HashMap中線程不安全問題有那些?原因分別是什麼?

JDK1.8之後如何鏈地址法,鏈表長度是多少的時候會轉換成紅黑樹。

節點個數是多少的時候,紅黑樹會退回鏈表。

爲什麼會選擇8作爲鏈表轉紅黑樹的閾值。

根據泊松分佈,在負載因子默認爲0.75的時候,單個hash槽內元素個數爲8的概率小於百萬分之一,所以將7作爲一個分水嶺,等於7的時候不轉換,大於等於8的時候才進行轉換,小於等於6的時候就化爲鏈表。

HashMap與HashTable有什麼區別?

有沒有了解過ConcurrentHashMap?

JDK1.8之後ConcurrentHashMap如何保證線程安全性?(CAS+synchronized),這裏還順便問了synchronized和可重入鎖的區別。

與JDK1.7相比有那些優化?

關於HashMap和ConcurrentHashMap的知識點我寫過了,就不做重複工作了,去看看我HashMap和ConcurrentHashMap相關的文章就好了(跟我介紹的內容基本一致,以我爲準):

說到synchronized,說些synchronized加載static關鍵字前和普通方法前的區別?

Synchronized修飾非靜態方法,實際上是對調用該方法的對象加鎖,俗稱“對象鎖”

Synchronized修飾靜態方法,實際上是對該類對象加鎖,俗稱“類鎖”。

  • 對象鎖鑰匙只能有一把才能互斥,才能保證共享變量的唯一性
  • 在靜態方法上的鎖,和 實例方法上的鎖,默認不是同樣的,如果同步需要制定兩把鎖一樣。
  • 關於同一個類的方法上的鎖,來自於調用該方法的對象,如果調用該方法的對象是相同的,那麼鎖必然相同,否則就不相同。比如 new A().x() 和 new A().x(),對象不同,鎖不同,如果A的單利的,就能互斥。
  • 靜態方法加鎖,能和所有其他靜態方法加鎖的 進行互斥
  • 靜態方法加鎖,和xx.class 鎖效果一樣,直接屬於類的

看你熟悉單例,介紹下單例模式並且說下單例懶漢式和餓漢式的區別?(手寫)

單例的介紹:

意圖:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

主要解決:一個全局使用的類頻繁地創建與銷燬。

何時使用:當您想控制實例數目,節省系統資源的時候。

如何解決:判斷系統是否已經有這個單例,如果有則返回,如果沒有則創建。

關鍵代碼:構造函數是私有的。

應用實例:

  • 一個班級只有一個班主任。
  • Windows 是多進程多線程的,在操作一個文件的時候,就不可避免地出現多個進程或線程同時操作一個文件的現象,所以所有文件的處理必須通過唯一的實例來進行。
  • 一些設備管理器常常設計爲單例模式,比如一個電腦有兩臺打印機,在輸出的時候就要處理不能兩臺打印機打印同一個文件。

優點:

  • 在內存裏只有一個實例,減少了內存的開銷,尤其是頻繁的創建和銷燬實例(比如管理學院首頁頁面緩存)。
  • 避免對資源的多重佔用(比如寫文件操作)。

缺點:沒有接口,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。

使用場景:

  • 要求生產唯一序列號。
  • WEB 中的計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來。
  • 創建的一個對象需要消耗的資源過多,比如 I/O 與數據庫的連接等。

注意事項:getInstance() 方法中需要使用同步鎖 synchronized (Singleton.class) 防止多線程同時進入造成 instance 被多次實例化。

懶漢

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  

    public static Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

餓漢

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}

懶漢式下如何保證線程安全?

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

創建線程安全的單例有那些實現方法?

雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking)

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}

登記式/靜態內部類

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}

枚舉

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

說一下JVM的內存模型?(每一個模塊都說)

方法區(Method Area)
方法區主要是放一下類似類定義、常量、編譯後的代碼、靜態變量等,在JDK1.7中,HotSpot VM的實現就是將其放在永久代中,這樣的好處就是可以直接使用堆中的GC算法來進行管理,但壞處就是經常會出現內存溢出,即PermGen Space異常,所以在JDK1.8中,HotSpot VM取消了永久代,用元空間取而代之,元空間直接使用本地內存,理論上電腦有多少內存它就可以使用多少內存,所以不會再出現PermGen Space異常。

堆(Heap)
幾乎所有對象、數組等都是在此分配內存的,在JVM內存中佔的比例也是極大的,也是GC垃圾回收的主要陣地,平時我們說的什麼新生代、老年代、永久代也是指的這片區域,至於爲什麼要進行分代後面會解釋。

虛擬機棧(Java Stack)
當JVM在執行方法時,會在此區域中創建一個棧幀來存放方法的各種信息,比如返回值,局部變量表和各種對象引用等,方法開始執行前就先創建棧幀入棧,執行完後就出棧。

本地方法棧(Native Method Stack)
和虛擬機棧類似,不過區別是專門提供給Native方法用的。

程序計數器(Program Counter Register)
佔用很小的一片區域,我們知道JVM執行代碼是一行一行執行字節碼,所以需要一個計數器來記錄當前執行的行數。

熟不熟悉垃圾回收算法?給我介紹一下垃圾回收算法有哪些?

標記清除

標記-清除算法將垃圾回收分爲兩個階段:標記階段和清除階段

在標記階段首先通過根節點(GC Roots),標記所有從根節點開始的對象,未被標記的對象就是未被引用的垃圾對象。然後,在清除階段,清除所有未被標記的對象。

複製算法

從根集合節點進行掃描,標記出所有的存活對象,並將這些存活的對象複製到一塊兒新的內存(圖中下邊的那一塊兒內存)上去,之後將原來的那一塊兒內存(圖中上邊的那一塊兒內存)全部回收掉

標記整理

複製算法的高效性是建立在存活對象少、垃圾對象多的前提下的。

這種情況在新生代經常發生,但是在老年代更常見的情況是大部分對象都是存活對象。如果依然使用複製算法,由於存活的對象較多,複製的成本也將很高。

分代收集算法

分代收集算法就是目前虛擬機使用的回收算法,它解決了標記整理不適用於老年代的問題,將內存分爲各個年代。一般情況下將堆區劃分爲老年代(Tenured Generation)和新生代(Young Generation),在堆區之外還有一個代就是永久代(Permanet Generation)。

在不同年代使用不同的算法,從而使用最合適的算法,新生代存活率低,可以使用複製算法。而老年代對象存活率搞,沒有額外空間對它進行分配擔保,所以只能使用標記清除或者標記整理算法。

如何判定一個對象是否應該回收。

爲了解決循環引用的問題,java中採取了正向可達的方式,主要是通過Roots對象作爲起點進行搜索,搜索走過的路徑稱爲“引用鏈”,當一個對象到 Roots 沒有任何的引用鏈相連時時,證明此對象不可用,當然被判定爲不可達的對象不一定就會成爲可回收對象。

被判定爲不可達的對象要成爲可回收對象必須至少經歷兩次標記過程,如果在這兩次標記過程中仍然沒有逃脫成爲可回收對象的可能性,則基本上就真的成爲可回收對象了,能否被回收其實主要還是要看finalize()方法有沒有與引用鏈上的對象關聯,如果在finalize()方法中有關聯則自救成功,改對象不可被回收,反之如果沒有關聯則成功被二次標記成功,就可以稱爲要被回收的垃圾了。

除了垃圾回收,還有那些工作會造成CPU負載過高(其實這裏給出的是一個場景,就是讓描述一下除了垃圾回收之外,還有那些工作會讓線上CPU佔用到百分之90-100,並且給出排查過程。)。

說一下CMS垃圾回收器和G1收集器的特點,和收集過程。

CMS收集器是一種以獲取最短回收停頓時間爲目標的收集器。基於“標記-清除”算法實現,它的運作過程如下:

  • 初始標記

  • 併發標記

  • 重新標記

  • 併發清除

    初始標記、從新標記這兩個步驟仍然需要“stop the world”,初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,熟讀很快,併發標記階段就是進行GC Roots Tracing,而重新標記階段則是爲了修正併發標記期間因用戶程序繼續運作而導致標記產生表動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長點,但遠比並發標記的時間短。
    CMS是一款優秀的收集器,主要優點:併發收集、低停頓。

缺點:

  • CMS收集器對CPU資源非常敏感。在併發階段,它雖然不會導致用戶線程停頓,但是會因爲佔用了一部分線程而導致應用程序變慢,總吞吐量會降低。

  • CMS收集器無法處理浮動垃圾,可能會出現“Concurrent Mode Failure(併發模式故障)”失敗而導致Full GC產生。

浮動垃圾:由於CMS併發清理階段用戶線程還在運行着,伴隨着程序運行自然就會有新的垃圾不斷產生,這部分垃圾出現的標記過程之後,CMS無法在當次收集中處理掉它們,只好留待下一次GC中再清理。這些垃圾就是“浮動垃圾”。

  • CMS是一款“標記--清除”算法實現的收集器,容易出現大量空間碎片。當空間碎片過多,將會給大對象分配帶來很大的麻煩,往往會出現老年代還有很大空間剩餘,但是無法找到足夠大的連續空間來分配當前對象,不得不提前觸發一次Full GC。

G1是一款面向服務端應用的垃圾收集器。G1具備如下特點:

  • 並行於併發:G1能充分利用CPU、多核環境下的硬件優勢,使用多個CPU(CPU或者CPU核心)來縮短stop-The-World停頓時間。部分其他收集器原本需要停頓Java線程執行的GC動作,G1收集器仍然可以通過併發的方式讓java程序繼續執行。

  • 分代收集:雖然G1可以不需要其他收集器配合就能獨立管理整個GC堆,但是還是保留了分代的概念。它能夠採用不同的方式去處理新創建的對象和已經存活了一段時間,熬過多次GC的舊對象以獲取更好的收集效果。

  • 空間整合:與CMS的“標記--清理”算法不同,G1從整體來看是基於“標記整理”算法實現的收集器;從局部上來看是基於“複製”算法實現的。

  • 可預測的停頓:這是G1相對於CMS的另一個大優勢,降低停頓時間是G1和CMS共同的關注點,但G1除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明確指定在一個長度爲M毫秒的時間片段內,

  • G1運作步驟:

1、初始標記;2、併發標記;3、最終標記;4、篩選回收

String a = “abc” ; 和String b = new String(“abc”) ; 是不是一樣的?爲什麼? 他們對應的內存空間分別是什麼?

不一樣

常量池
指的是在編譯期確定,並被保存在已編譯的字節碼文件中的一些數據,它包括類、方法、接口等中的常量,存放字符串常量和基本類型常量(public static final)。
棧(stack)

主要保存基本數據類型(或者叫內置類型)(char、byte、short、int、long、float、double、boolean)和對象的引用,數據可以共享,速度僅次於寄存器(register),快於堆。
所以他們在 == 的時候是false

說一下JVM創建對象的過程。

常量池中定位類的符號引用

​ ↓

檢查符號引用所代表的類是否已被加載,解析和初始化過 →

​ ↓ ↓

分配內存(類加載完成後,內存需求確定) ← 加載

​ ↓

根據java堆是否規整(GC方法)選擇分配方法

​ ↙ ↘

指針碰撞 空閒列表

​ ↓

分配內存的併發保證(指針更新的原子性)

​ ↙ ↘

CAS+失敗重試 按照線程劃分在不同的空間中進行TLAB -XX:+UseTLAB -XX:-UseTLAB

​ ↓

內存空間初始化爲0值,保證對象的實例字段可以不賦初值就可以使用。

​ ↓

設置對象頭信息(Object Header):引用指針,元數據,hash值,GC分代年齡,鎖相關

​ ↓

執行對象方法

說一下byte a = 127, byte b = 127; a+=b 和a = a+b的區別分別會出現什麼問題。

a+=b 會出現負數。

a=a+b 會直接報錯。

強制類型提升造成的

是否熟悉mysql? 說一下mysql的隔離級別和對應的問題。

Read Uncommitted(讀取未提交內容)

在該隔離級別,所有事務都可以看到其他未提交事務的執行結果。本隔離級別很少用於實際應用,因爲它的性能也不比其他級別好多少。讀取未提交的數據,也被稱之爲髒讀(Dirty Read)。

Read Committed(讀取提交內容)

這是大多數數據庫系統的默認隔離級別(但不是MySQL默認的)。它滿足了隔離的簡單定義:一個事務只能看見已經提交事務所做的改變。這種隔離級別 也支持所謂的不可重複讀(Nonrepeatable Read),因爲同一事務的其他實例在該實例處理其間可能會有新的commit,所以同一select可能返回不同結果。

Repeatable Read(可重讀)

這是MySQL的默認事務隔離級別,它確保同一事務的多個實例在併發讀取數據時,會看到同樣的數據行。不過理論上,這會導致另一個棘手的問題:幻讀 (Phantom Read)。簡單的說,幻讀指當用戶讀取某一範圍的數據行時,另一個事務又在該範圍內插入了新行,當用戶再讀取該範圍的數據行時,會發現有新的“幻影” 行。InnoDB和Falcon存儲引擎通過多版本併發控制(MVCC,Multiversion Concurrency Control)機制解決了該問題。

Serializable(可串行化)

這是最高的隔離級別,它通過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。簡言之,它是在每個讀的數據行上加上共享鎖。在這個級別,可能導致大量的超時現象和鎖競爭。這四種隔離級別採取不同的鎖類型來實現,若讀取的是同一個數據的話,就容易發生問題。例如:

  • 髒讀(Drity Read):某個事務已更新一份數據,另一個事務在此時讀取了同一份數據,由於某些原因,前一個RollBack了操作,則後一個事務所讀取的數據就會是不正確的。
  • 不可重複讀(Non-repeatable read):在一個事務的兩次查詢之中數據不一致,這可能是兩次查詢過程中間插入了一個事務更新的原有的數據。
  • 幻讀(Phantom Read):在一個事務的兩次查詢中數據筆數不一致,例如有一個事務查詢了幾列(Row)數據,而另一個事務卻在此時插入了新的幾列數據,先前的事務在接下來的查詢中,就有幾列數據是未查詢出來的,如果此時插入和另外一個事務插入的數據,就會報錯。

什麼是MVCC,主要是爲了做什麼?

MVCC(Mutil-Version Concurrency Control),就是多版本併發控制。MVCC 是一種併發控制的方法,一般在數據庫管理系統中,實現對數據庫的併發訪問。

在Mysql的InnoDB引擎中就是指在已提交讀(READ COMMITTD)和可重複讀(REPEATABLE READ)這兩種隔離級別下的事務對於SELECT操作會訪問版本鏈中的記錄的過程。

這就使得別的事務可以修改這條記錄,反正每次修改都會在版本鏈中記錄。SELECT可以去版本鏈中拿記錄,這就實現了讀-寫,寫-讀的併發執行,提升了系統的性能。

我們的數據庫當中如何做的優化?

  • 對查詢進行優化,要儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。

  • 應儘量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描

  • 應儘量避免在 where 子句中使用 != 或 <> 操作符,否則將引擎放棄使用索引而進行全表掃描。

  • 應儘量避免在 where 子句中使用 or 來連接條件,如果一個字段有索引,一個字段沒有索引,將導致引擎放棄使用索引而進行全表掃描

  • in 和 not in 也要慎用,否則會導致全表掃描

  • like模糊全匹配也將導致全表掃描

說一下mybaits和hibernate的區別。

共同點:

MyBaits和HiBernate都是通過ORM對象關係映射框架,都是持久層數據框架。

不同點:

  • HiBernate它重量級的框架,而MyBaits是輕量級的框架。
  • HiBernate對JDBC的封裝比較深,對開發者些sql的能力不是很高,只需要通過Hql語句操作對象即可完成數據的持久化操作了。
  • MyBaits也是對JDBC的封裝,但是沒有Hibernate那麼深,它的sql語句,都再配置裏,可以通過重寫配置裏sql,來實現數據優化,實施起來也比較方便。
  • 處理大數據的時候,建議使用MyBaits它優化sql語句更方便。

Hibernate狀態的轉換關係。

最新的Hibernate文檔中爲Hibernate對象定義了四種狀態(原來是三種狀態,面試的時候基本上問的也是三種狀態),分別是:瞬時態(new, or transient)、持久態(managed, or persistent)、遊離態(detached)和移除態(removed,以前Hibernate文檔中定義的三種狀態中沒有移除態),如下圖所示,就以前的Hibernate文檔中移除態被視爲是瞬時態。

  • 瞬時態:當new一個實體對象後,這個對象處於瞬時態,即這個對象只是一個保存臨時數據的內存區域,如果沒有變量引用這個對象,則會被JVM的垃圾回收機制回收。

    這個對象所保存的數據與數據庫沒有任何關係,除非通過Session的save()、saveOrUpdate()、persist()、merge()方法把瞬時態對象與數據庫關聯,並把數據插入或者更新到數據庫,這個對象才轉換爲持久態對象。

  • 持久態:持久態對象的實例在數據庫中有對應的記錄,並擁有一個持久化標識(ID)。

    對持久態對象進行delete操作後,數據庫中對應的記錄將被刪除,那麼持久態對象與數據庫記錄不再存在對應關係,持久態對象變成移除態(可以視爲瞬時態)。

    持久態對象被修改變更後,不會馬上同步到數據庫,直到數據庫事務提交。

  • 遊離態:當Session進行了close()、clear()、evict()或flush()後,實體對象從持久態變成遊離態,對象雖然擁有持久和與數據庫對應記錄一致的標識值,但是因爲對象已經從會話中清除掉,對象不在持久化管理之內,所以處於遊離態(也叫脫管態)。

    遊離態的對象與臨時狀態對象是十分相似的,只是它還含有持久化標識。

說一下Spring的理解,IOC和AOP在項目裏是怎麼用的。

Spring是一個開源框架,處於MVC模式中的控制層,它能應對需求快速的變化,其主要原因它有一種面向切面編程(AOP)的優勢,其次它提升了系統性能,因爲通過依賴倒置機制(IOC),系統中用到的對象不是在系統加載時就全部實例化,而是在調用到這個類時纔會實例化該類的對象,從而提升了系統性能。

這兩個優秀的性能使得Spring受到許多J2EE公司的青睞,如阿里裏中使用最多的也是Spring相關技術。

Spring的優點:

  • 降低了組件之間的耦合性,實現了軟件各層之間的解耦。

  • 可以使用容易提供的衆多服務,如事務管理,消息服務,日誌記錄等。

  • 容器提供了AOP技術,利用它很容易實現如權限攔截、運行期監控等功能。

Spring中AOP技術是設計模式中的動態代理模式,只需實現jdk提供的動態代理接口InvocationHandler,所有被代理對象的方法都由InvocationHandler接管實際的處理任務。

面向切面編程中還要理解切入點、切面、通知、織入等概念。

AOP的兩種實現方式,並且說一下哪一個效率更高一些,爲什麼。

兩種方式:一種是JDK動態代理,另一種是CGLib的方式。

JDK動態代理具體實現原理:

  • 通過實現InvocationHandlet接口創建自己的調用處理器;

  • 通過爲Proxy類指定ClassLoader對象和一組interface來創建動態代理;

  • 通過反射機制獲取動態代理類的構造函數,其唯一參數類型就是調用處理器接口類型;

  • 通過構造函數創建動態代理類實例,構造時調用處理器對象作爲參數參入;

JDK動態代理是面向接口的代理模式,如果被代理目標沒有接口那麼Spring也無能爲力,Spring通過Java的反射機制生產被代理接口的新的匿名實現類,重寫了其中AOP的增強方法。

CGLib動態代理:

CGLib是一個強大、高性能的Code生產類庫,可以實現運行期動態擴展java類,Spring在運行期間通過 CGlib繼承要被動態代理的類,重寫父類的方法,實現AOP面向切面編程呢。

兩者對比:

JDK動態代理是面向接口的。

CGLib動態代理是通過字節碼底層繼承要代理類來實現(如果被代理類被final關鍵字所修飾,那麼抱歉會失敗)。

性能:

關於兩者之間的性能的話,JDK動態代理所創建的代理對象,在以前的JDK版本中,性能並不是很高,雖然在高版本中JDK動態代理對象的性能得到了很大的提升,但是他也並不是適用於所有的場景。

主要體現在如下的兩個指標中:

  • CGLib所創建的動態代理對象在實際運行時候的性能要比JDK動態代理高不少,有研究表明,大概要高10倍;

  • 但是CGLib在創建對象的時候所花費的時間卻比JDK動態代理要多很多,有研究表明,大概有8倍的差距;

  • 因此,對於singleton的代理對象或者具有實例池的代理,因爲無需頻繁的創建代理對象,所以比較適合採用CGLib動態代理,反正,則比較適用JDK動態代理。

說一些Spring的事務傳播機制。

傳播記住有如下幾種:

public interface TransactionDefinition {
    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;
    int PROPAGATION_NESTED = 6;
}
  • PROPAGATION_REQUIRED:如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。這是最常見的選擇。
  • PROPAGATION_SUPPORTS:支持當前事務,如果當前沒有事務,就以非事務方式執行。
  • PROPAGATION_MANDATORY:使用當前的事務,如果當前沒有事務,就拋出異常。
  • PROPAGATION_REQUIRES_NEW:新建事務,如果當前存在事務,把當前事務掛起。
  • PROPAGATION_NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
  • PROPAGATION_NEVER:以非事務方式執行,如果當前存在事務,則拋出異常。
  • PROPAGATION_NESTED:如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行與PROPAGATION_REQUIRED類似的操作。

常用的主要有Required,RequiresNew,Nested三種。

  • Required:簡單理解就是事務方法會判斷是否存在事務,有事務就用已有的,沒有就重新開啓一個。
  • RequiresNew:簡單理解就是開啓新事務,若當前已有事務,掛起當前事務。新開啓的事務和之前的事務無關,擁有自己的鎖和隔離級別,可以獨立提交和回滾,內層事務執行期間,外層事務掛起,內層事務執行完成後,外層事務恢復執行。
  • Nested:簡單理解就是嵌套事務,如果外部事務回滾,則嵌套事務也會回滾!!!外部事務提交的時候,嵌套它纔會被提交。嵌套事務回滾不會影響外部事務。子事務是上層事務的嵌套事務,在子事務執行之前會建立savepoint,嵌套事務的回滾會回到這個savepoint,不會造成父事務的回滾。

如果想事務一起執行可以用Required滿足大部分場景,如果不想讓執行的子事務的結果影響到父事務的提交可以將子事務設置爲RequiresNew。

你有什麼想問我的麼?

我們京東\百度平時有技術分享這樣的活動麼?

有的你放心,技術分享我們都會不同的同事輪流進行不同技術棧的分享。

好的,那我沒什麼問題了,期待能和您一起共事。

總結

所有的題目都是粉絲提供的面試真題,我把所有的題目都查了下答案總結了一下,其實大家可以看出,很多題目都是我平時文章寫的內容,我基本上就是跟着面試的思路寫的。

而且面試的題目都很基礎,是不是覺得京東百度的面試其實也不難,我們好好準備下真心隨便進的那種,都是基礎,其實平時多留心下,都不需要突擊啥的。

白嫖不好,創作不易,各位的點贊就是丙丙創作的最大動力,我們下篇文章見,文末圖片有福利

持續更新,未完待續……


文章每週持續更新,可以微信搜索「 三太子敖丙 」第一時間閱讀,回覆【資料】【面試】有我準備的一線大廠面試資料和文章,本文 GitHub https://github.com/JavaFamily 已經收錄,有大廠面試完整考點,歡迎Star。

你知道的越多,你不知道的越多

發佈了46 篇原創文章 · 獲贊 7955 · 訪問量 55萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章