常見面試題及解答

https://www.cnblogs.com/ryan304/p/11578691.html

1Java 基礎

 

1、HashMap的源碼,實現原理,JDK8中對HashMap做了怎樣的優化。

1) HashMap實現Map接口,元素以鍵值對的方式存儲,並且允許使用null  作爲key和value,因爲HashMap的key不允許重複,所以只能有一個鍵是 null,並不推薦使用,因爲容易出問題並且很難排查。

 

2)HashMap不能保證放入元素的順序,所以是無序的,HashMap初始長度16,每次擴充翻倍。

JDK1.7的HashMap是使用數組加鏈表進行存儲,每一個鍵值對用一個Entry來存儲,Entry中還有hash和next屬性,hash是key的hash值,next則是指向下一個Entry的指針。HashMap的初始容量是16,即有一個長度爲16的數組,用來存儲每個Entry鏈表的頭結點。每一個Entry存儲的位置是由key.hashCode()%len獲得,假設有A,B,C三個鍵值對並且每個key的hash值對16取模都是1,A先進來就存儲在Entry[1],再進來B則B.next=A、Entry[1]=B。C相同。也就是說數組中存儲的是最後插入的元素。所以說放入元素是無序的。

JDK1.8的HashMap加入了紅黑樹,當每個單鏈表的長度大於閾值(8)時,則將鏈表轉化爲紅黑樹。並且1.8之後,新插入的元素都放在了鏈表的尾部。

 

3) HashMap是線程不安全的,可以使用Collections.synchronizedMap()獲 取一個線程安全的集合,Collections.synchronizedMap()實際上是定義一個synchronizedMap的內部類,這個內部類實現了Map接口並在方法上加上synchronized關鍵字。就是操作的HashMap的時候自動添synchronized。

 

 

2、HaspMap擴容是怎樣擴容的,爲什麼都是2的N次冪的大小。

1) 如果超過閾值,會自動擴充兩倍,然後重新計算HashMap中各個Entry的位置。

2) 都是2的N次冪是因爲在新插入操作的時候,位置計算是由(n-1)&hash決定的,(n-1)&hash實際上相當於hash%(n-1),只不過&運算更快速。HashMap中的hash=((h = key.hashCode()) ^ (h >>> 16),hashCode是9位十進制也就是36位二進制。右位移16位,正好是32bit的一半,自己的高半區和低半區做異或,就是爲了混合原始哈希碼的高位和低位,以此來加大低位的隨機性。而且混合後的低位摻雜了高位的部分特徵,這樣高位的信息也被變相保留下來。這樣大多數的hashCode的分佈已經很不錯了。

 

3、HashMap,HashTable,ConcurrentHashMap的區別。

   HashMap的key和value都可以爲空,線程不安全,初始size16,每次擴容翻倍,先插入後擴容,插入後長度達到總數的75%則擴容,計算index(n-1)&hash,迭代器是fail-fast的(在遍歷一個集合時,當集合結構被修改,會拋出Concurrent Modification Exception)

HashTable的key和value都不能爲空,線程不安全,初始size11,每次擴容size=oldSize*2+1,計算index方法 (hash & 0x7FFFFFFF) % tab.length,迭代器不是fail-fast

ConcurrentHashMap線程安全,分段加鎖,段內擴容。需鎖定整個map時按順序鎖定各個分段,然後按順序釋放鎖。

 

4、極高併發下HashTable和ConcurrentHashMap哪個性能更好,爲什麼,如何實現的。

ConcurrentHashMap更好,分段鎖,HashTable鎖整個table。

 

5、HashMap在高併發下如果沒有處理線程安全會有怎樣的安全隱患,具體表現是什麼。

1)put是會見檢查Map容量是否足夠,不夠則擴充,擴充後會把數組從老的hash表移到新的hash表,多個線程同時同時操作可能會形成循環鏈表,所以get()時會出現無限循環

2)造成迭代的fail-fast

 

6、java中四種修飾符的限制範圍。

 

 

7、Object類中的方法。

 hash,finalize,notify,notifyAll,wait....

8、接口和抽象類的區別,注意JDK8的接口可以有實現。

(1)抽象類可以有構造方法,接口中不能有構造方法。

(2)抽象類中可以有普通成員變量,接口中沒有普通成員變量

(3)抽象類中可以包含靜態方法,接口中不能包含靜態方法

(4) 一個類可以實現多個接口,但只能繼承一個抽象類。

(5)接口可以被多重實現,抽象類只能被單一繼承

(6)如果抽象類實現接口,則可以把接口中方法映射到抽象類中作爲抽象方法而不必實現,而在抽象類的子類中實現接口中方法

 

9、動態代理的兩種方式,以及區別。

 (1)JDK代理使用的是反射機制實現aop的動態代理,CGLIB代理使用字節碼處理框架asm,通過修改字節碼生成子類。所以jdk動態代理的方式創建代理對象效率較高,執行效率較低,cglib創建效率較低,執行效率高;

 (2)JDK動態代理機制是委託機制,具體說動態實現接口類,在動態生成的實現類裏面委託hanlder去調用原始實現類方法,CGLIB則使用的繼承機制,具體說被代理類和代理類是繼承關係,所以代理類是可以賦值給被代理類的,如果被代理類有接口,那麼代理類也可以賦值給接口

10、Java序列化的方式。

 

11、傳值和傳引用的區別,Java是怎麼樣的,有沒有傳值引用。

 

12、一個ArrayList在循環過程中刪除,會不會出問題,爲什麼。

會遺漏元素,例如刪除了a[2],刪除後a[3]就到了a[2]的位置上,繼續遍歷是從a[3]開始,那麼如果之前的a[3]符合刪除條件也不會被刪除。反向遍歷刪除是可以的,用迭代器刪除單線程可以多線程有可能觸發fail-fast。

 

2JVM

 

1、JVM的內存結構。

 程序計數器:當前線程所執行的字節碼的行號指示器,是一塊較小的內存空間

 Java虛擬機棧:線程私有的,生命週期同線程相同。虛擬機棧描述的是java方法執行的內存模型--每個方法在執行的同時都會創建一個棧幀用於存儲局部變量表、操作數棧,動態鏈接、方法出口等信息。

 本地方法棧:與虛擬機棧類似,只是本地方法棧只服務於Native方法。

 java堆:線程共享,佔用內存最大,GC的主要區域。所有的對象實例都要在這裏分配內存

 方法區:線程共享,Java虛擬機規範把方法區描述爲堆的的一個邏輯部分,但別名是非堆,與堆區分。方法區用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯             後的代碼等數據。

2、JVM方法棧的工作過程,方法棧和本地方法棧有什麼區別。

   見1

3、JVM的棧中引用如何和堆中的對象產生關聯。

 對象的實例保存在堆上,對象的元數據(instantKlass)保存在方法區,對象的引用保存在棧上。

4、可以瞭解一下逃逸分析技術。

 

5、GC的常見算法,CMS以及G1的垃圾回收過程,CMS的各個階段哪兩個是Stop the world的,CMS會不會產生碎片,G1的優勢。

 (1)判斷對象已死:可達性分析算法,從root對象開始到當前對象沒有任何調用鏈,則證明當前對象不可用。不可用不一定“非死不可”,需要至少經歷兩次標記過程,然後纔會被虛擬機自動建立的、低優先級的Finalizer線程去執行(只是執行並不等待執行結束)。

 (2)清除算法:

 標記-清除算法:標記所有需要回收的對象,統一回收。效率不高並且會產生大量不連續的內存碎片

 複製算法:將內存分爲兩塊,每次只使用一塊,將當前使用塊中的存活對象複製到另一塊中,然後在把當前塊清理掉。可用內存縮小嚴重

 標記-整理算法:標記存活的對象都向一端移動,然後清理掉端邊界以外的內存。

  (3)堆一般分爲年輕代和老年代:

    年輕代一般採用複製算法,有一個eden區和兩個survior區(From和To),大小比例通常是8:1(年輕代中80%的對象都是朝生夕死)。每次只使eden區和一個survior(From)區。每個GC周 期都會將eden區的所有存活對象複製到To中,而From中仍然存活的並且年齡未到達閾值的對象會被複制到To中,超齡的複製到老年代。這時eden和From是空的,From就會轉換成“To”,To"則當做“From”。知道To中區域滿了則會移動所有存活對象到老年代。

    (4)垃圾收集器

       CMS和G1

  

6、標記清除和標記整理算法的理解以及優缺點。

 見上

7、eden survivor區的比例,爲什麼是這個比例,eden survivor的工作過程。

 見上

8、JVM如何判斷一個對象是否該被GC,可以視爲root的都有哪幾種類型。

 被啓動類(bootstrap 加載器)加載的類和創建的對象;
 JavaStack 中的引用的對象 (棧內存中引用的對象);
 方法區中靜態引用指向的對象;
 方法區中常量引用指向的對象;
 Native 方法中 JNI 引用的對象。

9、強軟弱虛引用的區別以及GC對他們執行怎樣的操作。

 不想打字了,見《深入理解java虛擬機》第2版第65頁3.2.3再談引用

10、Java是否可以GC直接內存。

 直接內存其實是jvm自定義的空間,直接內存本身不受gc的影響,但是由於有對象在堆引用這這塊內存,那麼受到gc的間接影響,典型的是java的代碼裏有system.gc去回收。

 堆外內存不同於直接內存,堆外內存會溢出,並且其垃圾回收依賴於代碼顯式調用System.gc()

11、Java類加載的過程。

 加載-驗證-準備-解析-初始化

12、雙親委派模型的過程以及優勢。

 

13、常用的JVM調優參數。

 

14、dump文件的分析。

 

15、Java有沒有主動觸發GC的方式(沒有)。

 

3數據結構與算法

 

1、B+樹

 

2、快速排序,堆排序,插入排序(八大排序算法)

 

3、一致性Hash算法,一致性Hash算法的應用

 

 

 

4多線程()

 

1、Java實現多線程有哪幾種方式。

  繼承Thread或者實現Runnable接口

2、Callable和Future的瞭解。

  Callable是跟Runnable類似的接口。不過Callable接口需要實現call方法,是有返回值的並且可以拋出異常。而實現Runnable接口需要實現run方法是沒有返回值的並無法拋出異常。

  Future是控制Callable的運行過程,參數是Callable的一個實例。

3、線程池的參數有哪些,在線程池創建一個線程的過程。

  核心線程數,最大線程數,空閒時間,時間單位,阻塞隊列,拒絕機制

4、volitile關鍵字的作用,原理。

 (1)Lock前綴的指令會引起處理器緩存寫回內存;

 (2)一個處理器的緩存回寫到內存會導致其他處理器的緩存失效;

 (3)當處理器發現本地緩存失效後,就會從內存中重讀該變量數據,即可以獲取當前最新值

5、synchronized關鍵字的用法,優缺點。

 

6、Lock接口有哪些實現類,使用場景是什麼。

 

7、可重入鎖的用處及實現原理,寫時複製的過程,讀寫鎖,分段鎖(ConcurrentHashMap中的segment)。

 

8、悲觀鎖,樂觀鎖,優缺點,CAS有什麼缺陷,該如何解決。

 

9、ABC三個線程如何保證順序執行。

 

10、線程的狀態都有哪些。

 

11、sleep和wait的區別。

 

12、notify和notifyall的區別。

 

13、ThreadLocal的瞭解,實現原理。

 

 

 

5分佈式

 

1、分佈式事務的控制。分佈式鎖如何設計。

 

2、分佈式session如何設計。

 

3、dubbo的組件有哪些,各有什麼作用。

 

4、zookeeper的負載均衡算法有哪些。

 

5、dubbo是如何利用接口就可以通信的。

 

 

 

6框架相關

 

1、SpringMVC的Controller是如何將參數和前端傳來的數據一一對應的。

 

2、Mybatis如何找到指定的Mapper的,如何完成查詢的。

 

3、Quartz是如何完成定時任務的。自定義註解的實現。

 

4、Spring使用了哪些設計模式。Spring的IOC有什麼優勢。

 

5、Spring如何維護它擁有的bean。

 

6、一些較新的東西JDK8的新特性,流的概念及優勢,爲什麼有這種優勢。

 

7、區塊鏈瞭解如何設計雙11交易總額面板,要做到高併發高可用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章