設計模式是老生常談的問題,有人工作多年卻對設計模式一竅不通,但是更多的人是懂一點點,但是不求甚解。其實這樣不好,暫且不說在工作中的應用,即便是在面試時,被面試官問到設計模式時一臉懵逼,是非常尷尬的事情。本文不廢話,不談大篇理論教學,只針對面試,給出設計模式的關鍵點,從應試的角度,讓大家認識和理解設計模式。
首先搞清楚一點,設計模式不是高深技術,不是奇淫技巧。設計模式只是一種設計思想,針對不同的業務場景,用不同的方式去設計程式碼結構,其最最本質的目的是為了解耦,延伸一點的話,還有為了可擴充套件性和健壯性,但是這都是建立在解耦的基礎之上。
我們都知道大名鼎鼎的GoF的21種設計模式,看過head first的這本書的人應該不少。針對這21種設計模式,面試官問出的問題可能千變萬化,讓人莫名的憂(dan)傷(teng),但是不要怕,只要你搞清楚了每種設計模式的關鍵點和精髓,就可以舉一反三,迎刃而解了。
今天我們來看看最簡單的單例模式。理論我就不介紹了,沒聽說過的可以去查一下。即便是最簡單的單例,也有關鍵點。舉個不太恰當地例子,看過修仙修道之類小說的同學都知道,一般陣法高手做陣都有一個或多個“陣眼”,同理,我們每種設計模式也有“陣眼”,那麼單例的“陣眼”在哪裡?各位莫慌,我們先來看看程式碼。根據單例模式的理論:保證系統中只有一個例項,於是我擼了以下程式碼
1 2 3 4 5 6 7 8 9 10 11 12 |
public class Singleton { private Singleton() {} //關鍵點0:建構函式是私有的 private static Singleton single = null; //關鍵點1:宣告單例物件是靜態的 public static Singleton GetInstance() //通過靜態方法來構造物件 { if (single == null) { //關鍵點2:判斷單例物件是否已經被構造 single = new Singleton(); } return single; } } |
好了,如果我是面試官,你是候選人,我要你擼個單例給我,你擼以上程式碼問我資詞不資詞,我肯定是不資詞的。為什麼?真的不是因為我想搞個大新聞,而是這段單例的程式碼一般情況下還是可以勉強執行,但是,遇到多執行緒的併發條件下就大清藥丸了。因為這段程式碼是執行緒不安全的,有可能給new出多個單例例項,都多個了,還是屁的“單例”啊。
好,廢話不多說,繼續擼程式碼,你可能會說:”不是說執行緒不安全嗎?小爺我加上執行緒安全判斷唄,度娘在手天下我有,來了您吶~~~“
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class Singleton { private Singleton() {} //關鍵點0:建構函式是私有的 private static Singleton single = null; //關鍵點1:宣告單例物件是靜態的 private static object obj= new object(); public static Singleton GetInstance() //通過靜態方法來構造物件 { if (single == null) //關鍵點2:判斷單例物件是否已經被構造 { lock(obj) //關鍵點3:加執行緒鎖 { single = new Singleton(); } } return single; } } |
好了,這回該消停了吧,鎖也加了,執行緒也安全了。But,你還是太連清。。。這樣依然有問題。問題在哪裡?就在關鍵點2,檢測單例是否被構造。雖然這裡判斷了一次,但是由於某些情況下,可能有延遲載入或者快取的原因,只有關鍵點2這一次判斷,仍然不能保證系統是否只建立了一個單例,也可能出現多個例項的情況,那麼怎麼辦呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class Singleton { private Singleton() {} //關鍵點0:建構函式是私有的 private static Singleton single = null; //關鍵點1:宣告單例物件是靜態的 private static object obj= new object(); public static Singleton GetInstance() //通過靜態方法來構造物件 { if (single == null) //關鍵點2:判斷單例物件是否已經被構造 { lock(obj) //關鍵點3:加執行緒鎖 { if(single == null) //關鍵點4:二次判斷單例是否已經被構造 { single = new Singleton(); } } } return single; } } |
所以,在判斷單例例項是否被構造時,需要檢測兩次,線上程鎖之前判斷一次,線上程鎖之後判斷一次,再去構造例項,這樣就萬無一失了。是不是很簡(Tu)單(Xue)呢?
最後,我們來歸納一下。下次面試別人再問你單例模式,你可以這樣說:
單例是為了保證系統中只有一個例項,其關鍵點有5
一.私有建構函式
二.宣告靜態單例物件
三.構造單例物件之前要加鎖(lock一個靜態的object物件)
四.需要兩次檢測單例例項是否已經被構造,分別在鎖之前和鎖之後
如果要你擼程式碼,你就擼最後這一段,完美~~~面試官要是個女的準想和你生猴子。。。
哦,別高興太早了,面試官要是個男的,也可能會問你下列問題
0.為何要檢測兩次?
如上面所述,有可能延遲載入或者快取原因,造成構造多個例項,違反了單例的初衷。
1.建構函式能否公有化?
不行,單例類的建構函式必須私有化,單例類不能被例項化,單例例項只能靜態呼叫
2.lock住的物件為什麼要是object物件,可以是int嗎?
不行,鎖住的必須是個引用型別。如果鎖值型別,每個不同的執行緒在宣告的時候值型別變數的地址都不一樣,那麼上個執行緒鎖住的東西下個執行緒進來會認為根本沒鎖,相當於每次都鎖了不同的門,沒有任何卵用。而引用型別的變數地址是相同的,每個執行緒進來判斷鎖多想是否被鎖的時候都是判斷同一個地址,相當於是鎖在通一扇門,起到了鎖的作用。
好了,這下估計男的都想跟你生猴子了。。。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式