java 常見問題總結(一)
Q:面向物件程式設計的四大特性及其含義?
對現實世界的事物進行概括,抽象為在計算機虛擬世界中有意義的實體
封裝:將某事物的屬性和行為包裝到物件中,構成一個不可分割的獨立實體,資料被保護在抽象資料型別的內部,並且儘可能地隱藏內部的細節,只保留一些對外介面使之與外部發生聯絡
繼承:子類繼承父類,不僅可以有父類原有的方法和屬性,也可以增加自己的或者重寫父類的方法及屬性
多型:允許不同類的物件對同一訊息做出各自的響應
Q:String、StringBuffer和StringBuilder的區別?
String是字串常量,而StringBuffer、StringBuilder都是字串變數,即String物件一建立後不可更改,而後兩者的物件是可更改的: StringBuffer是執行緒安全的,而StringBuilder是非執行緒安全的,這是由於StringBuffer對方法加了同步鎖或者對呼叫的方法加了同步鎖 String更適用於少量的字串操作的情況,StringBuilder適用於單執行緒下在字元緩衝區進行大量操作的情況,StringBuffer適用於多執行緒下在字元緩衝區進行大量操作的情況
Q:String a=""和String a=new String("")的的關係和異同?
通過String a=""直接賦值的方式得到的是一個字串常量,存在於常量池;注意,相同內容的字串在常量池中只有一個,即如果池已包含內容相等的字串會返回池中的字串,反之會將該字串放入池中
通過new String("")建立的字串不是常量是例項物件,會在堆記憶體開闢空間並存放資料,且每個例項物件都有自己的地址空間
引申:對於用String a=""和String a=new String("")兩種方式定義的字串,判斷使用equals()、"=="比較結果是什麼
Q:Object的equal()和==的區別?
equals():是Object的公有方法,具體含義取決於如何重寫,比如String的equals()比較的是兩個字串的內容是否相同
"==" :對於基本資料型別來說,比較的是兩個變數值是夠是否相等,對於引用型別來說,比較的是兩個物件的記憶體地址是否相同
引申:對於用String a=""和String a=new String("")兩種方式定義的字串,判斷使用equals()、"=="比較結果是什麼
Q:裝箱、拆箱什麼含義?
裝箱就是自動將基本資料型別轉換為包裝器型別,拆箱就是自動將包裝器型別轉換為基本資料型別
Q:int和Integer的區別?
Integer是int的包裝類,int則是java的一種基本資料型別 Integer變數必須例項化後才能使用,而int變數不需要 Integer實際是物件的引用,當new一個Integer時,實際上是生成一個指標指向此物件;而int則是直接儲存資料值 Integer的預設值是null,int的預設值是0
Q:遇見過哪些執行時異常?異常處理機制知道哪些?
對Throwable異常進行分類說明每種異常的特點和常見問題,簡述幾種常見異常處理機制,詳見Java基礎之異常機制
(1) Throwable繼承層次結構,可見分成兩大類Error和Exception:
Error(錯誤):指程式 無法 恢復的異常情況,表示執行應用程式中較嚴重的問題;發生於虛擬機器自身、或者在虛擬機器試圖執行應用時,如Virtual MachineError(Java虛擬機器執行錯誤)、NoClassDefFoundError(類定義錯誤);屬於 不可查 異常,即不強制程式設計師必須處理,即使不處理也不會出現語法錯誤。
Exception(異常):指程式 有可能 恢復的異常情況,表示程式本身可以處理的異常。又分兩大類:
RuntimeException(執行時異常):由程式 自身 的問題導致產生的異常;如NullPointerException(空指標異常)、IndexOutOfBoundsException(下標越界異常);屬於 不可查 異常。
非執行時異常:由程式 外部 的問題引起的異常;除了RuntimeException以外的異常,如FileNotFoundException(檔案不存在異常);屬於 可查 異常,即強制程式設計師必須進行處理,如果不進行處理則會出現語法錯誤。

(2)常見的異常處理機制有:
捕捉異常:由系統自動丟擲異常,即try捕獲異常->catch處理異常->finally 最終處理
丟擲異常:在方法中將異常物件 顯性 地丟擲,之後異常會沿著呼叫層次向上丟擲,交由呼叫它的方法來處理。配合throws宣告丟擲的異常和throw丟擲異常
自定義異常:繼承Execption類或其子類
Q:什麼是反射,有什麼作用和應用?Java基礎之泛型&反射
含義 :在執行狀態中,對於任意一個類都能知道它的所有屬性和方法,對於任何一個物件都能夠呼叫它的任何一個方法和屬性。
功能 :動態性,體現在:在執行時判斷任意一個類所具有的屬性和方法; 在執行時判斷任意一個物件所屬的類;在執行時構造任意一個類的物件;在執行時呼叫任意一個物件的方法;生成動態代理
引申:是否在專案中使用過反射機制,有什麼優缺點
Q:什麼是內部類?有什麼作用?靜態內部類和非靜態內部類的區別?
內部類就是定義在另外一個類裡面的類。它隱藏在外部類中,封裝性更強,不允許除外部類外的其他類訪問它;但它可直接訪問外部類的成員。靜態內部類和非靜態內部類的區別有:
靜態內部類是指被宣告為static的內部類,可不依賴外部類例項化;而非靜態內部類需要通過生成外部類來間接生成。
靜態內部類只能訪問外部類的靜態成員變數和靜態方法,而非靜態內部類由於持有對外部類的引用,可以訪問外部類的所用成員
引申:談談匿名內部類
Q:final、finally、finalize()分別表示什麼含義
final關鍵字表示不可更改,具體體現在:
final修飾的變數必須要初始化,且賦初值後不能再重新賦值
final修飾的方法不能被子類重寫
final修飾的類不能被繼承
finally:和try、catch成套使用進行異常處理,無論是否捕獲或處理異常,finally塊裡的語句都會被執行,在以下4種特殊情況下,finally塊才不會被執行:
在finally語句塊中發生了異常
在前面的程式碼中用了System.exit()退出程式
程式所在的執行緒死亡
關閉CPU
finalize():是Object中的方法,當垃圾回收器將回收物件從記憶體中清除出去之前會呼叫finalize(),但此時並不代表該回收物件一定會“死亡”,還有機會“逃脫”
Q:重寫和過載的區別?
參考回答:重寫表示子類重寫父類的方法;過載表示有多個同名函式同時存在,區別在於有不同的引數個數或型別
引申:談談動態分派和靜態分派
Q:抽象類和介面的異同?
使用上的區別:一個類只能繼承一個抽象類卻可以實現多個介面
設計上的區別:介面是對行為的抽象,無需有子類的前提,是自上而下的設計理念;抽象類是對類的抽象,建立於相似子類之上,是自下而上的設計理念
Q:為什麼匿名內部類中使用區域性變數要用final修飾?
一方面,由於方法中的區域性變數的生命週期很短,一旦方法結束變數就要被銷燬,為了保證在內部類中能找到外部區域性變數,通過final關鍵字可得到一個外部變數的引用;另一方面,通過final關鍵字也不會在內部類去做修改該變數的值,保護了資料的一致性。
Q:Object有哪些公有方法?
equals(): 和==作用相似
hashCode():用於雜湊查詢,重寫了equals()一般都要重寫該方法
getClass(): 獲取Class物件
wait():讓當前執行緒進入等待狀態,並釋放它所持有的鎖
notify()¬ifyAll(): 喚醒一個(所有)正處於等待狀態的執行緒
toString():轉換成字串
引申:equals()和==的不同、在synchronized 同步程式碼塊裡wait()和notify()¬ifyAll()如何配合、hashCode()和equals()的關係、獲取Class物件還有什麼方法
Q:Java集合框架中有哪些類?都有什麼特點
可將Java集合框架大致可分為Set、List、Queue 和Map四種體系
Set:代表 無序、不可重複 的集合,常見的類如HashSet、TreeSet
List:代表 有序、可重複 的集合,常見的類如動態陣列ArrayList、雙向連結串列LinkedList、可變陣列Vector
Map:代表具有 對映關係 的集合,常見的類如HashMap、LinkedHashMap、TreeMap
Queue:代表一種 佇列 集合
Q:集合、陣列、泛型的關係,並比較
(1)集合和陣列的區別:
陣列元素可以是基本型別,也可以是物件;陣列長度限定;陣列只能儲存一種型別的資料元素
集合元素只能是物件;集合長度可變;集合可儲存不同種的資料元素
(2)泛型相比與集合的好處在於它 安全簡單 。具體體現在提供編譯時的強型別檢查,而不用等到執行;可避免類型別強制轉換
Q:ArrayList和LinkList的區別?
ArrayList 的底層結構是 陣列 ,可用索引實現快速查詢;是動態陣列,相比於陣列容量可實現動態增長
LinkedList 底層結構是 連結串列 ,增刪速度快;是一個 雙向迴圈 連結串列,也可以被當作堆疊、佇列或雙端佇列
Q:ArrayList和Vector的區別?
安全,建議在單執行緒中才使用ArrayList,而在多執行緒中可以選擇Vector或者CopyOnWriteArrayList;預設初始容量為10,每次擴容為原來的1.5倍
Vector 使用了synchronized關鍵字,是 執行緒安全 的,比ArrayList開銷更大,訪問更慢;預設初始容量為10,預設每次擴容為原來的2倍,可通過 capacityIncrement 屬性設定
Q:HashSet和TreeSet的區別?
HashSet 不能保證元素的排列順序;使用 Hash演算法 來儲存集合中的元素,有良好的存取和查詢效能;通過equal()判斷兩個元素是否相等,並兩個元素的hashCode()返回值也相等
TreeSet 是SortedSet介面的實現類,根據元素 實際值的大小 進行排序;採用 紅黑樹 的資料結構來儲存集合元素;支援兩種排序方法: 自然排序 (預設情況)和 定製排序 。前者通過實現 Comparable介面 中的compareTo()比較兩個元素之間大小關係,然後按升序排列;後者通過實現 Comparator介面 中的compare()比較兩個元素之間大小關係,實現定製排列
Q:HashMap和Hashtable的區別?
HashMap 基於AbstractMap類,實現了Map、 Cloneable (能被克隆)、 Serializable (支援序列化)介面; 非執行緒安全 ;允許存在一個為null的key和任意個為null的value;採用 連結串列雜湊 的資料結構,即陣列和連結串列的結合;初始容量為16,填充因子預設為0.75,擴容時是當前容量翻倍,即2capacity
Hashtable 基於Map介面和Dictionary類; 執行緒安全 ,開銷比HashMap大,如果多執行緒訪問一個Map物件,使用Hashtable更好;不允許使用null作為key和value;底層基於雜湊表結構;初始容量為11,填充因子預設為0.75,擴容時是容量翻倍+1,即2capacity+1
Q:HashMap在put、get元素的過程?體現了什麼資料結構?
向Hashmap中put元素時,首先判斷key是否為空,為空則直接呼叫putForNullKey(),不為空則計算key的hash值得到該元素在陣列中的下標值;如果陣列在該位置處沒有元素,就直接儲存;如果有,還要比較是否存在相同的key,存在的話就覆蓋原來key的value,否則將該元素儲存在鏈頭,先儲存的在鏈尾。
從Hashmap中get元素時,計算key的hash值找到在陣列中的對應的下標值,返回該key對應的value即可,如果有衝突就遍歷該位置連結串列尋找key相同的元素並返回對應的value
由此可看出HashMap採用 連結串列雜湊 的資料結構,即陣列和連結串列的結合,在Java8後又結合了紅黑樹,當連結串列元素超過8個將連結串列轉換為紅黑樹
Q:如何解決Hash衝突?
開放定址法:常見的線性探測方式,在衝突發生時,順序查看錶中下一單元,直到找出一個空單元或查遍全表
鏈地址法:將有衝突陣列位置生出連結串列
建立公共溢位區:將雜湊表分為基本表和溢位表兩部分,和基本表發生衝突的元素一律填入溢位表
再雜湊法:構造多個不同的雜湊函式,有衝突使用下一個雜湊函式計算hash值
Q:如何保證HashMap執行緒安全?什麼原理?
思路:這裡回答一種辦法,使用ConcurrentHashMap
參考回答:ConcurrentHashMap是執行緒安全的HashMap,它採取鎖分段技術,將資料分成一段一段的儲存,然後給每一段資料配一把鎖,當一個執行緒佔用鎖訪問其中一個段資料的時候,其他段的資料也能被其他執行緒訪問。在JDK1.8中對ConcurrentHashmap做了兩個改進:
取消segments欄位,直接採用transient volatile HashEntry<K,V>[] table儲存資料,將陣列元素作為鎖,對每一行資料進行加鎖,可減少併發衝突的概率
資料結構由“陣列+單向連結串列”變為“陣列+單向連結串列+紅黑樹”,使得查詢的時間複雜度可以降低到O(logN),改進一定的效能。
引申:LinkHashMap執行緒安全的底層實現
Q:HashMap是有序的嗎?如何實現有序?
技術點:LinkHashMap
思路:這裡回答一種辦法,使用LinkedHashMap
HashMap是無序的,而LinkedHashMap是有序的HashMap,預設為插入順序,還可以是訪問順序,基本原理是其內部通過Entry維護了一個雙向連結串列,負責維護Map的迭代順序
引申:LinkHashMap有序的底層實現
Q:HashMap是如何擴容的?如何避免擴容?
HashMap幾個預設值,初始容量為16、填充因子預設為0.75、擴容時容量翻倍。也就是說當HashMap中元素個數超過16*0.75=12時會把陣列的大小擴充套件為2*16=32,然後重新計算每個元素在陣列中的位置
由於每次擴容還需要重新計算元素Hash值,損耗效能,所以建議在使用HashMap時,最好先估算Map的大小,設定初始值,避免頻繁擴容
Q:hashcode()的作用,與equal()有什麼區別?
hashCode()用於計算物件的Hash值,確認物件在雜湊儲存結構中的儲存地址。和equal()的區別:
equals()比較兩個物件的地址值是否相等 ;hashCode()得到的是物件的儲存位置,可能不同物件會得到相同值
有兩個物件,若equals()相等,則hashcode()一定相等;hashcode()不等,則equals()一定不相等;hashcode()相等,equals()可能相等、可能不等
使用equals()比較兩個物件是否相等效率較低,最快辦法是先用hashCode()比較,如果hashCode()不相等,則這兩個物件肯定不相等;如果hashCode()相等,此時再用equal()比較,如果equal()也相等,則這兩個物件的確相等,反之併發
Q:同步和非同步、阻塞和非阻塞的概念
同步和非同步體現的是訊息的通知機制:所謂同步,方法A呼叫方法B後必須等到方法B返回結果才能繼續後面的操作;所謂非同步,方法A呼叫方法B後可讓方法B在呼叫結束後通過回撥等方式通知方法A
阻塞和非阻塞側重於等待訊息時的狀態:所謂阻塞,就是在結果返回之前讓當前執行緒掛起;所謂非阻塞,就是在等待時可做其他事情,通過輪詢去詢問是否已返回結果
Q:Thread的join()有什麼作用?
參考回答:Thread的join()的含義是等待該執行緒終止,即將掛起呼叫執行緒的執行,直到被呼叫的物件完成它的執行。比如存在兩個執行緒t1和t2,下述程式碼表示先啟動t1,直到t1的任務結束,才輪到t2啟動。
t1.start();
t1.join();
t2.start();
Q:執行緒的有哪些狀態?
可分條解釋每種狀態的特點以及如何轉換。詳見 ofollow,noindex">要點提煉| 理解JVM之記憶體模型&執行緒
參考回答:在任意一個時間點,一個執行緒只能有且只有其中的一種狀態:
新建 (New):執行緒建立後尚未啟動
執行 (Runable):包括正在執行(Running)和等待著CPU為它分配執行時間(Ready)兩種
無限期等待 (Waiting):該執行緒不會被分配CPU執行時間,要等待被其他執行緒顯式地喚醒。以下方法會讓執行緒陷入無限期等待狀態:
沒有設定Timeout引數的Object.wait()
沒有設定Timeout引數的Thread.join()
LockSupport.park()
限期等待 (Timed Waiting):該執行緒不會被分配CPU執行時間,但在一定時間後會被系統自動喚醒。以下方法會讓執行緒進入限期等待狀態:
Thread.sleep()
設定了Timeout引數的Object.wai()
設定了Timeout引數的Thread.join()
LockSupport.parkNanos()
LockSupport.parkUntil()
阻塞 (Blocked):執行緒被阻塞。和等待狀態不同的是,阻塞狀態表示在等待獲取到一個 排他鎖 ,在另外一個執行緒放棄這個鎖的時候發生;而等待狀態表示在等待一段 時間 或者 喚醒動作 的發生,在程式等待進入同步區域的時候發生。
結束 (Terminated):執行緒已經結束執行

Q:什麼是執行緒安全?保障執行緒安全有哪些手段?
思路:詳見 要點提煉| 理解JVM之執行緒安全&鎖優化
參考回答:執行緒安全就是當多個執行緒訪問一個物件時,如果不用考慮這些執行緒在執行時環境下的排程和交替執行,也不需要進行額外的同步,或者在呼叫方進行任何其他的協調操作,呼叫這個物件的行為都可以獲得正確的結果,那這個物件是執行緒安全的。保證執行緒安全可從多執行緒三特性出發:
原子性 (Atomicity):單個或多個操作是要麼全部執行,要麼都不執行
Lock:保證同時只有一個執行緒能拿到鎖,並執行申請鎖和釋放鎖的程式碼
synchronized:對執行緒加獨佔鎖,被它修飾的類/方法/變數只允許一個執行緒訪問
可見性 (Visibility):當一個執行緒修改了共享變數的值,其他執行緒能夠立即得知這個修改
volatile:保證新值能 立即 同步到主記憶體,且每次使用前立即從主記憶體重新整理;
synchronized:在釋放鎖之前會將工作記憶體新值更新到主存中
有序性 (Ordering):程式程式碼按照指令順序執行
volatile: 本身就包含了禁止指令重排序的語義
synchronized:保證一個變數在同一個時刻只允許一條執行緒對其進行lock操作,使得持有同一個鎖的兩個同步塊只能序列地進入
Q:ReentrantLock和synchronized的區別?
技術點:執行緒安全(ReentrantLock、synchronized)
思路:詳見 要點提煉| 理解JVM之執行緒安全&鎖優化
參考回答: ReentrantLock與synchronized的 不同 在於ReentrantLock:
等待可中斷 :當持有鎖的執行緒長期不釋放鎖的時候,正在等待的執行緒可以選擇放棄等待,改為處理其他事情。
公平鎖 :多個執行緒在等待同一個鎖時,必須按照申請鎖的時間順序來依次獲得鎖。而synchronized是非公平的,即在鎖被釋放時,任何一個等待鎖的執行緒都有機會獲得鎖。ReentrantLock預設情況下也是非公平的,但可以通過帶布林值的建構函式改用公平鎖。
鎖繫結多個條件 :一個ReentrantLock物件可以通過多次呼叫newCondition()同時繫結多個Condition物件。而在synchronized中,鎖物件wait()和notify()或notifyAl()只能實現一個隱含的條件,若要和多於一個的條件關聯不得不額外地新增一個鎖。
Q:synchronized和volatile的區別?
synchronized能保證操作的原子性,而volatile不可以,假設執行緒A和執行緒B同時讀取到變數a值,A修改a後將值更新到主記憶體,同時B也修改a值會覆蓋A的修改操作 synchronized可修飾變數、方法和類,而volatile只能修飾變數 synchronized可能會造成執行緒阻塞,而volatile不會造成執行緒的阻塞
Q:synchronized同步程式碼塊還有同步方法本質上鎖住的是誰?為什麼?
參考回答:本質上鎖住的是物件。在java虛擬機器中,每個物件和類在邏輯上都和一個監視器相關聯,synchronized本質上是對一個物件監視器的獲取。當執行同步程式碼塊或同步方法時,執行方法的執行緒必須先獲得該物件的監視器,才能進入同步程式碼塊或同步方法;而沒有獲取到的執行緒將會進入阻塞佇列,直到成功獲取物件監視器的執行緒執行結束並釋放鎖後,才會喚醒阻塞佇列的執行緒,使其重新嘗試對物件監視器的獲取。
Q:sleep()和wait()的區別?
sleep()來自Thread類;wait()來自Object類 sleep()用於執行緒控制自身流程;而wait()用於執行緒間通訊,配合notify()/notifyAll()在同步程式碼塊或同步方法裡使用 sleep()的執行緒不會釋放物件鎖;wait()會釋放物件鎖進入等待狀態,使得其他執行緒能使用同步程式碼塊或同步方法