Java高級面試題及答案

List和Set比較,各自的子類比較

對比一:Arraylist與LinkedList的比較前端

一、ArrayList是實現了基於動態數組的數據結構,由於地址連續,一旦數據存儲好了,查詢操做效率會比較高(在內存裏是連着放的)。
算法

二、由於地址連續, ArrayList要移動數據,因此插入和刪除操做效率比較低。 spring

三、LinkedList基於鏈表的數據結構,地址是任意的,因此在開闢內存空間的時候不須要等一個連續的地址,對於新增和刪除操做add和remove,LinedList比較佔優點。數據庫

四、由於LinkedList要移動指針,因此查詢操做性能比較低。編程

適用場景分析:後端

當須要對數據進行對此訪問的狀況下選用ArrayList,當須要對數據進行屢次增長刪除修改時採用LinkedList。數組

對比二:ArrayList與Vector的比較緩存

一、Vector的方法都是同步的,是線程安全的,而ArrayList的方法不是,因爲線程的同步必然要影響性能。所以,ArrayList的性能比Vector好。
二、當Vector或ArrayList中的元素超過它的初始大小時,Vector會將它的容量翻倍,而ArrayList只增長50%的大小,這樣。ArrayList就有利於節約內存空間。安全

三、大多數狀況不使用Vector,由於性能很差,可是它支持線程的同步,即某一時刻只有一個線程可以寫Vector,避免多線程同時寫而引發的不一致性。bash

四、Vector能夠設置增加因子,而ArrayList不能夠。

適用場景分析:

一、Vector是線程同步的,因此它也是線程安全的,而ArrayList是線程異步的,是不安全的。若是不考慮到線程的安全因素,通常用ArrayList效率比較高。

二、若是集合中的元素的數目大於目前集合數組的長度時,在集合中使用數據量比較大的數據,用Vector有必定的優點。

對比三:HashSet與TreeSet的比較

1.TreeSet 是二叉樹實現的,Treeset中的數據是自動排好序的,不容許放入null值 。

2.HashSet 是哈希表實現的,HashSet中的數據是無序的,能夠放入null,但只能放入一個null,二者中的值都不能重複,就如數據庫中惟一約束 。

3.HashSet要求放入的對象必須實現HashCode()方法,放入的對象,是以hashcode碼做爲標識的,而具備相同內容的String對象,hashcode是同樣,因此放入的內容不能重複。可是同一個類的對象能夠放入不一樣的實例。

適用場景分析:

HashSet是基於Hash算法實現的,其性能一般都優於TreeSet。咱們一般都應該使用HashSet,在咱們須要排序的功能時,咱們才使用TreeSet。


HashMap和ConcurrentHashMap的區別

一、HashMap不是線程安全的,而ConcurrentHashMap是線程安全的。

二、ConcurrentHashMap採用鎖分段技術,將整個Hash桶進行了分段segment,也就是將這個大的數組分紅了幾個小的片斷segment,並且每一個小的片斷segment上面都有鎖存在,那麼在插入元素的時候就須要先找到應該插入到哪個片斷segment,而後再在這個片斷上面進行插入,並且這裏還須要獲取segment鎖。

三、ConcurrentHashMap讓鎖的粒度更精細一些,併發性能更好。


JVM的內存結構

根據 JVM 規範,JVM 內存共分爲虛擬機棧、堆、方法區、程序計數器、本地方法棧五個部分。

一、Java虛擬機棧:

線程私有;每一個方法在執行的時候會建立一個棧幀,存儲了局部變量表,操做數棧,動態鏈接,方法返回地址等;每一個方法從調用到執行完畢,對應一個棧幀在虛擬機棧中的入棧和出棧。

二、堆:

線程共享;被全部線程共享的一塊內存區域,在虛擬機啓動時建立,用於存放對象實例。

三、方法區:

線程共享;被全部線程共享的一塊內存區域;用於存儲已被虛擬機加載的類信息,常量,靜態變量等。

四、程序計數器:

線程私有;是當前線程所執行的字節碼的行號指示器,每條線程都要有一個獨立的程序計數器,這類內存也稱爲「線程私有」的內存。

五、本地方法棧:

線程私有;主要爲虛擬機使用到的Native方法服務。


強引用,軟引用和弱引用的區別

強引用:

只有這個引用被釋放以後,對象纔會被釋放掉,只要引用存在,垃圾回收器永遠不會回收,這是最多見的New出來的對象。

軟引用:

內存溢出以前經過代碼回收的引用。軟引用主要用戶實現相似緩存的功能,在內存足夠的狀況下直接經過軟引用取值,無需從繁忙的真實來源查詢數據,提高速度;當內存不足時,自動刪除這部分緩存數據,從真正的來源查詢這些數據。

弱引用:

第二次垃圾回收時回收的引用,短期內經過弱引用取對應的數據,能夠取到,當執行過第二次垃圾回收時,將返回null。弱引用主要用於監控對象是否已經被垃圾回收器標記爲即將回收的垃圾,能夠經過弱引用的isEnQueued方法返回對象是否被垃圾回收器標記。


springmvc的核心是什麼,請求的流程是怎麼處理的,控制反轉怎麼實現的

核心:

控制反轉和麪向切面

請求處理流程:

一、首先用戶發送請求到前端控制器,前端控制器根據請求信息(如URL)來決定選擇哪個頁面控制器進行處理並把請求委託給它,即之前的控制器的控制邏輯部分;

二、頁面控制器接收到請求後,進行功能處理,首先須要收集和綁定請求參數到一個對象,並進行驗證,而後將命令對象委託給業務對象進行處理;處理完畢後返回一個ModelAndView(模型數據和邏輯視圖名);

三、前端控制器收回控制權,而後根據返回的邏輯視圖名,選擇相應的視圖進行渲染,並把模型數據傳入以便視圖渲染;

四、前端控制器再次收回控制權,將響應返回給用戶。

控制反轉如何實現:

咱們每次使用spring框架都要配置xml文件,這個xml配置了bean的id和class。

spring中默認的bean爲單實例模式,經過bean的class引用反射機制能夠建立這個實例。

所以,spring框架經過反射替咱們建立好了實例而且替咱們維護他們。

A須要引用B類,spring框架就會經過xml把B實例的引用傳給了A的成員變量。


BIO、NIO和AIO的區別

Java BIO : 同步並阻塞,服務器實現模式爲一個鏈接一個線程,即客戶端有鏈接請求時服務器端就須要啓動一個線程進行處理,若是這個鏈接不作任何事情會形成沒必要要的線程開銷,固然能夠經過線程池機制改善。

Java NIO : 同步非阻塞,服務器實現模式爲一個請求一個線程,即客戶端發送的鏈接請求都會註冊到多路複用器上,多路複用器輪詢到鏈接有I/O請求時才啓動一個線程進行處理。

Java AIO: 異步非阻塞,服務器實現模式爲一個有效請求一個線程,客戶端的I/O請求都是由OS先完成了再通知服務器應用去啓動線程進行處理。

NIO比BIO的改善之處是把一些無效的鏈接擋在了啓動線程以前,減小了這部分資源的浪費(由於咱們都知道每建立一個線程,就要爲這個線程分配必定的內存空間)

AIO比NIO的進一步改善之處是將一些暫時可能無效的請求擋在了啓動線程以前,好比在NIO的處理方式中,當一個請求來的話,開啓線程進行處理,但這個請求所須要的資源尚未就緒,此時必須等待後端的應用資源,這時線程就被阻塞了。

適用場景分析:

BIO方式適用於鏈接數目比較小且固定的架構,這種方式對服務器資源要求比較高,併發侷限於應用中,JDK1.4之前的惟一選擇,但程序直觀簡單易理解,如以前在Apache中使用。

NIO方式適用於鏈接數目多且鏈接比較短(輕操做)的架構,好比聊天服務器,併發侷限於應用中,編程比較複雜,JDK1.4開始支持,如在 Nginx,Netty中使用。

AIO方式使用於鏈接數目多且鏈接比較長(重操做)的架構,好比相冊服務器,充分調用OS參與併發操做,編程比較複雜,JDK7開始支持,在成長中,Netty曾經使用過,後來放棄。


爲何要用線程池

那先要明白什麼是線程池

線程池是指在初始化一個多線程應用程序過程當中建立一個線程集合,而後在須要執行新的任務時重用這些線程而不是新建一個線程。

使用線程池的好處

一、線程池改進了一個應用程序的響應時間。因爲線程池中的線程已經準備好且等待被分配任務,應用程序能夠直接拿來使用而不用新建一個線程。

二、線程池節省了CLR 爲每一個短生存週期任務建立一個完整的線程的開銷並能夠在任務完成後回收資源。

三、線程池根據當前在系統中運行的進程來優化線程時間片。

四、線程池容許咱們開啓多個任務而不用爲每一個線程設置屬性。

五、線程池容許咱們爲正在執行的任務的程序參數傳遞一個包含狀態信息的對象引用。

六、線程池能夠用來解決處理一個特定請求最大線程數量限制問題。


悲觀鎖和樂觀鎖的區別,怎麼實現

悲觀鎖:一段執行邏輯加上悲觀鎖,不一樣線程同時執行時,只能有一個線程執行,其餘的線程在入口處等待,直到鎖被釋放。

樂觀鎖:一段執行邏輯加上樂觀鎖,不一樣線程同時執行時,能夠同時進入執行,在最後更新數據的時候要檢查這些數據是否被其餘線程修改了(版本和執行初是否相同),沒有修改則進行更新,不然放棄本次操做。

悲觀鎖的實現:

begin;/begin work;/start transaction; (三者選一就能夠)
//1.查詢出商品信息
select status from t_goods where id=1 for update;
//2.根據商品信息生成訂單
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status爲2
update t_goods set status=2;
//4.提交事務
commit;/commit work;
複製代碼

樂觀鎖的實現:

1.查詢出商品信息
select (status,status,version) from t_goods where id=#{id}
2.根據商品信息生成訂單
3.修改商品status爲2
update t_goods 
set status=2,version=version+1
where id=#{id} and version=#{version};
複製代碼


什麼是線程死鎖?死鎖如何產生?如何避免線程死鎖?

死鎖的介紹:

線程死鎖是指因爲兩個或者多個線程互相持有對方所須要的資源,致使這些線程處於等待狀態,沒法前往執行。當線程進入對象的synchronized代碼塊時,便佔有了資源,直到它退出該代碼塊或者調用wait方法,才釋放資源,在此期間,其餘線程將不能進入該代碼塊。當線程互相持有對方所須要的資源時,會互相等待對方釋放資源,若是線程都不主動釋放所佔有的資源,將產生死鎖。

死鎖的產生的一些特定條件:

一、互斥條件:進程對於所分配到的資源具備排它性,即一個資源只能被一個進程佔用,直到被該進程釋放 。

二、請求和保持條件:一個進程因請求被佔用資源而發生阻塞時,對已得到的資源保持不放。

三、不剝奪條件:任何一個資源在沒被該進程釋放以前,任何其餘進程都沒法對他剝奪佔用。

四、循環等待條件:當發生死鎖時,所等待的進程一定會造成一個環路(相似於死循環),形成永久阻塞。

如何避免:

一、加鎖順序:

當多個線程須要相同的一些鎖,可是按照不一樣的順序加鎖,死鎖就很容易發生。若是能確保全部的線程都是按照相同的順序得到鎖,那麼死鎖就不會發生。固然這種方式須要你事先知道全部可能會用到的鎖,然而總有些時候是沒法預知的。

二、加鎖時限:

加上一個超時時間,若一個線程沒有在給定的時限內成功得到全部須要的鎖,則會進行回退並釋放全部已經得到的鎖,而後等待一段隨機的時間再重試。可是若是有很是多的線程同一時間去競爭同一批資源,就算有超時和回退機制,仍是可能會致使這些線程重複地嘗試但卻始終得不到鎖。

三、死鎖檢測:

死鎖檢測即每當一個線程得到了鎖,會在線程和鎖相關的數據結構中(map、graph等等)將其記下。除此以外,每當有線程請求鎖,也須要記錄在這個數據結構中。死鎖檢測是一個更好的死鎖預防機制,它主要是針對那些不可能實現按序加鎖而且鎖超時也不可行的場景。


這一篇先總結這些,歡迎關注個人公衆號

相關文章
相關標籤/搜索