單例模式是23種GOF模式中最簡單,也是最經常出現的一種設計模式,也是面試官最常愛考的一種模式,為什么呢?
因為單例模式足夠簡單,撰寫一個單例模式代碼幾分鐘就能搞定,所以設計模式中面試官通常會選取單例模式作為出題,
下面把單例模式分幾個點,分別說說哪些地方面試官能考你?
開始之前,記得點贊收藏加關注哦 ,需要下載PDF版本和獲取更多知識點、面試題的朋友可以點一點下方鏈接免費領取
鏈接:點這里!!! 799215493 暗號:CSDN

單例模式的意義
通常面試官會很籠統的問你,什么是單例模式?單例模式用來解決了什么痛點?沒有單例模式我們會怎么辦?單例模式他有什么缺點嗎?
單例模式是最簡單的設計模式之一,屬于創建型模式,它提供了一種創建物件的方式,確保只有單個物件被創建,這個設計模式主要目的是想在整個系統中只能出現類的一個實體,即一個類只有一個物件,
單例模式的解決的痛點就是節約資源,節省時間從兩個方面看:
1.由于頻繁使用的物件,可以省略創建物件所花費的時間,這對于那些重量級的物件而言,是很重要的.
2.因為不需要頻繁創建物件,我們的GC壓力也減輕了,而在GC中會有STW(stop the world),從這一方面也節約了GC的時間
單例模式的缺點:簡單的單例模式設計開發都比較簡單,但是復雜的單例模式需要考慮執行緒安全等并發問題,引入了部分復雜度,
擴展:從你的回答中能進行哪些擴展呢?我們談到了GC,有可能這時候就會問你GC,STW等知識,談缺點的時候談到了復雜的單例模式,這個時候可能會問你讓你設計一個優秀的單例模式你會怎么設計,會怎么實作?
單例模式的設計
通常這里面試官會問你單例模式怎么設計,需要看重哪些方面?一般來說單例模式有哪些實作方式?
設計單例模式的時候一般需要考慮幾種因素:
- 執行緒安全
- 延遲加載
- 代碼安全:如防止序列化攻擊,防止反射攻擊(防止反射進行私有方法呼叫)
- 性能因素
一般來說,我們去網上百度去搜大概有7,8種實作,,下面列舉一下需要重點知道的
餓漢,懶漢(執行緒安全,執行緒非安全),雙重檢查(DCL)(重點),內部類,以及列舉(重點),
下面比對下各個實作:

擴展:我們上面說到了各個模式的實作,這個時候很有可能會叫你手寫各個模式的代碼,當然也有可能會問你執行緒安全,代碼安全等知識,
餓漢模式
餓漢模式的代碼如下:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
餓漢模式代碼比較簡單,物件在類中被定義為private static,通過getInstance(),通過java的classLoader機制保證了單例物件唯一,
擴展:
有可能會問instance什么時候被初始化?
Singleton類被加載的時候就會被初始化,java虛擬機規范雖然沒有強制性約束在什么時候開始類加載程序,但是對于類的初始化,虛擬機規范則嚴格規定了有且只有四種情況必須立即對類進行初始化,遇到new、getStatic、putStatic或invokeStatic這4條位元組碼指令時,如果類沒有進行過初始化,則需要先觸發其初始化,
生成這4條指令最常見的java代碼場景是:
1)使用new關鍵字實體化物件
2)讀取一個類的靜態欄位(被final修飾、已在編譯期把結果放在常量池的靜態欄位除外)
3)設定一個類的靜態欄位(被final修飾、已在編譯期把結果放在常量池的靜態欄位除外)
4)呼叫一個類的靜態方法
class的生命周期?
class的生命周期一般來說會經歷加載、連接、初始化、使用、和卸載五個階段
class的加載機制
這里可以聊下classloader的雙親委派模型,
雙重檢查DCL
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;
}
}
synchronized同步塊里面能夠保證只創建一個物件,但是通過在synchronized的外面增加一層判斷,就可以在物件一經創建以后,不再進入synchronized同步塊,這種方案不僅減小了鎖的粒度,保證了執行緒安全,性能方面也得到了大幅提升,
同時這里要注意一定要說volatile,這個很關鍵,volatile一般用于多執行緒的可見性,但是這里是用來防止指令重排序的,
擴展:
為什么需要volatile?volatile有什么用?
- 首先要回答可見性,這個是毋庸質疑的,然后可能又會考到java記憶體模型,
- 防止指令重排序: 防止new Singleton時指令重排序導致其他執行緒獲取到未初始化完的物件,instance = new Singleton()這句,這并非是一個原子操作,事實上在 JVM 中這句話大概做了下面 3 件事情,
1.給 instance 分配記憶體
2.呼叫 Singleton 的建構式來初始化成員變數
3.將instance物件指向分配的記憶體空間(執行完這步 instance 就為非 null 了)
但是在 JVM 的即時編譯器中存在指令重排序的優化,也就是說上面的第二步和第三步的順序是不能保證的,最終的執行順序可能是 1-2-3 也可能是 1-3-2,如果是后者,則在 3 執行完畢、2 未執行之前,被執行緒二搶占了,這時 instance 已經是非 null 了(但卻沒有初始化),所以執行緒二會直接回傳 instance,然后使用,然后報錯, - 順便也可以說下volatie原理用記憶體屏障
講講synchronized和volatile的區別
這里可以從synchroized能保證原子性,volatile不能保證說起,以及講下synchroized是重量級鎖,甚至可以所以下他和Lock的區別等等,
執行緒安全一般怎么實作的?
- 互斥同步,如lock,synchroized
- 非阻塞同步,如cas,
- 不同步,如threadLocal,區域變數,
列舉類
public enum Singleton{
INSTANCE;
}
默認列舉實體的創建是執行緒安全的,所以不需要擔心執行緒安全的問題,同時他也是《Effective Java》中推薦的模式,最后通過列舉類,他能自動避免序列化/反序列化攻擊,以及反射攻擊(列舉類不能通過反射生成),
總結
單例模式雖然看起來簡單,但是設計的Java基礎知識非常多,如static修飾符、synchronized修飾符、volatile修飾符、enum等,這里的每一個知識點都可以變成面試官下手的考點,而單例只是作為一個引子,考到最后看你到底掌握了多少,看你的廣度和深度到底是怎么樣的,
寫在最后
我這里準備了一線大廠面試資料和我原創的超硬核PDF技術檔案,以及我為大家精心準備的多套簡歷模板(不斷更新中),希望大家都能找到心儀的作業!
有需要的朋友可以點一點下方鏈接免費領取
鏈接:點這里!!! 799215493 暗號:CSDN


轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/224786.html
標籤:java
上一篇:JAVA學習筆記(四)城堡游戲
