來次面試吧?準備好沒,GO!
問:自我介紹下吧。(開個玩笑。。。往下)請問你用過單例模式嗎?什麼是單例?
答:用過啊,單例模式就是隻建立一個執行個體。
問:噢?那是單線程,還是多線程下都是呢?
答:這個類在JVM裡就一個執行個體(這樣回答也許會更好)
(注:通常面試官,到這裡,就會讓你寫一個單例,當然我們不,你看這篇文章,就是不讓你再網上找其他重複的資料了。)
問:說說你用的場景吧
答:........
問:說說單例的幾種類型?或者說何時對執行個體的初始化?
問:畫過單例的類圖嗎?會畫嗎?
答:(需要嗎?)
問:寫個單例的實作吧?
類名:SingleTon
執行個體:uniqueInstance 簡稱ust吧。(這裡我插入一句程式設計規範:起名不要吝惜把執行個體代表的意思表達清楚,名字可稍微長點,這裡就是想偷懶)
實作1:
public class Singleton{
private final static Singleton ust = new Singleton();
private Singleton() {}
public static Singleton getInstance(){
return ust;
}
}
注:這裡順便說明一下文法。通常final與static同時出現,習慣讓final在前
分析:
1. 這裡需要加final嗎? 是的,因為java反射,可以改變private描述的變量
2. 有書裡把這種方式稱為餓漢單例模式(另一個種叫懶漢單例),并且已經為人所接受
3. 在多線程方面表現出了他優勢,不需要擔心方法重複裡延時建立帶來的原子性(這樣說難了解,其實就是出現兩個或多個執行個體)
4. 這個,不符合我們習慣的 用到時再執行個體化的原則(不過這沒關系。。。)
看,其實你發現這還是個不錯的單例,那麼其實有個最好的實作,最好的實作:
單元素的枚舉類型已經成為Singleton的最佳實踐
即使面對複雜的序列化或者反射攻擊,絕對防止多次執行個體化,還有他的簡潔和優雅。
----好吧,面試結束了。 其實你對單例的了解還是不錯的,而且你已經得到了最好的答案,有興趣徹底玩轉單例嗎,繼續聽我唠叨。
我們看看懶漢單例模式:
實作2:
// private final static Singleton ust = null;
// 不該加final,這裡明顯有偷懶嫌疑,複制上面的例子,又測試不夠,以後盡量避免類似問題。
// 8月15日修正
private static Singleton ust = null;
private Singleton(){}
public staic Singleton getInstance(){
//建議null==ust的方式,能幫助更快的發現錯誤。
//部分老程式員的習慣,其實許多IDE會發現些低級錯誤。
if(ust==null){//A
ust = new Singleton();//B
}
return ust;
}
}
分析:
1. 懶漢模式,做到了需要時建立執行個體
2. 他遇到了尴尬的問題,因為當兩個線程分開運作到A,然後進入了if塊,可能就建立了2個執行個體,草稿的是,你已經初始了一些資料。
改進一下:
實作3:
private volatile static Singleton ust;
private Singleton(){}
public staic Singleton getInstance(){
synchronized(Singleton.class){
if(ust==null){
ust= new Singleton();
}
}
return ust;
分析:
1. 如果你不了解synchronized 的位置,就不用單例模式這麼多寫法,不如學習基礎
2. volatile 確定ust被執行個體化後,多個線程正确處理。他失去了JVM必要的代碼優化,如果不是多線程,就不要用
3. 這個叫做 “雙檢查加鎖”,單例最後一種方式
綜合讨論會:
小明: 單例目前一共談到懶漢和餓漢兩種,還有雙檢查加鎖,最好的應該是單元素的枚舉類型
小剛: 是的,回答了開頭說的幾種單例,那麼哪些場景應該用單例呢?
小明: 我知道,有線程池,緩存,處理偏好設定,系統資料庫,日志對象等等
小剛: 對,我對java比較了解,我知道Runtime.getRuntime()。
C(為嗎我叫C):我知道有java.lang.reflect.Proxy類
小明:有什麼共同點呢,為什麼用?是遵循對象盡量少建立原則?
C:這是什麼意思?
小剛:這很簡單,不過這說法有點問題。因為對象占記憶體,有要造成垃圾回收,GC的時候JVM可是隻幹這個麻煩事
小明:是啊!
小剛:我想我知道,某些對象最好隻有一個執行個體,多了會有問題産生。
C:什麼問題?
小剛:比如緩存,你從哪個執行個體裡拿緩存呢?
小明:是的啊。。。
結束讨論會,總結一下吧。不,等等,還要補充兩句:
1. 單例模式定義:確定一個隻有一個執行個體,并提供一個全局通路點
2. 如果getInstance()方法對應用程式不會額外負擔,或者說影響不大,那寫成怎樣,其實沒太大所謂。但是如果頻繁運作,就要仔細考慮,因為一個同步,可能使得執行效率下降100倍
繼續總結,還差個UML圖呢,不妨在上個枚舉的例子吧,枚舉構造器預設私有嗎?
枚舉就算了,是不是預設構造器,自己研究下吧。。。呵呵
這回真總結了:
1. 單例,有懶漢,餓漢,雙檢查加鎖3種常見用法
2. 單例模式,是因為如果多了,會造成資料遺漏等麻煩
3. 最好的單例,單元素的枚舉類型
呵呵,其實,就這些,本來想先寫工廠的,因為去面試,遇到某些對單例了解比較淺,解釋起來費勁,于是先以單例開篇,請關注下篇工廠模式。
本文轉自 wws5201985 51CTO部落格,原文連結:http://blog.51cto.com/yjplxq/931861,如需轉載請自行聯系原作者