單例模式是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中一個類只有一個實例。 ...
單例模式是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中一個類只有一個實例。
早晨,老王來到公司,發現小蔡正對著電腦屏幕發呆,於是走到小蔡身後,發現小蔡正對著一個寶寶照發呆。
老王拍了一下小蔡肩膀,問:“這個是誰啊?沒聽說你有男朋友啊。難道是私生子?”
小蔡回頭,呸了老王一下,說:“這是我二表姑的侄子的姐夫的姑姑的妹妹的舅舅的兒媳婦的堂哥的娃娃。”
老王還沒回過神來,小蔡又說:“寶寶真可愛,要是我以後要寶寶了,我一定只要一個,我要帶她到處去耍,給他買各種漂亮的衣服,各種好玩的玩具,各種吧嗒吧嗒吧嗒……(此處省略數千字)”
老王心想:“都說一個女人等於500只鴨子,這個小蔡何止500只鴨子,簡直就是5000只鴨子,而且全是話癆型鴨子。”
老王趁小蔡兩句話之間喘氣的時機,趕緊打斷了小蔡:“你說到只要一個寶寶,我給你講講程式代碼只要一個寶寶的方法,好不?”
小蔡一聽,很好奇:“程式代碼,也有一個寶寶的說法?”
老王說眼看將話題轉移了,趕緊接著說:“是啊,那就是單例模式,它可以讓整個程式系統里,只存在唯一的實例對象。從而大幅節省記憶體資源。”
老王不給小蔡搭話的機會,緊接著演示起了代碼:“單例模式,分為懶漢式和餓漢式,我們先來看看懶漢式。”
//懶漢式
public class Lazybones {
//實例對象
private static Lazybones instances = null;
//私有的構造函數,阻止實例化對象
private Lazybones(){}
//如果發現沒有實例對象,就構造一個
//如果有實例對象,直接返回
public static Lazybones getInstances(){
if(instances == null){
instances = new Lazybones();
}
return instances;
}
}
小蔡很好奇,問:“代碼也有懶漢?”
老王說:“對啊,在使用到的時候才實例化,這就叫懶漢式。這種方式有個好處就是在不使用的時候不會占用記憶體空間。但是這裡也有一個問題。”
小蔡問:“什麼問題啊?”
老王說:“在大併發,多線程的環境下,假如有多個線程同時執行到getInstances()
方法,第一個線程執行if
語句,還沒有完成構造時,第二個線程也執行到if
這裡,這時候instance
依然為空。這樣線程一和線程二會同時產生兩個實例。所以懶漢式的單例模式,並不是線程安全的 。”
小蔡又問:“難道餓漢式是線程安全的?”
老王說:“對的,餓漢式單例模式的確是線程安全的。咱們來看代碼。”
//餓漢式
public class Hungry {
//不管三七二十一,直接實例化一個對象
private static Hungry instances = new Hungry();
//私有的構造函數,阻止實例化對象
private Hungry(){}
//直接返回已經實例化了的對象
public static Hungry getInstances(){
return instances;
}
}
老王說:“你看,餓漢式單例模式,不管是否使用,直接就將對象實例化在那兒放著,要用的時候直接使用,這樣就不用再去判斷,所以餓漢式單例模式是線程安全的。”
小蔡問:“老王,我們怎麼驗證這個這個對象是否單例呢?我們又看不到記憶體里的分配情況。”
老王說:“這個我們就得靠對象的哈希碼了。因為哈希碼是用來在散列存儲結構中確定對象的存儲地址的。簡單理解,就是一個對象的hash code和記憶體地址是一一對應的,只要hash code值相同,那麼就是同一記憶體地址。在Java里,一個對象的toString()
方法預設返回這個對象的hash code。我們來看代碼。”
public class SingleCheck {
public static void main(String[] args) {
//通過餓漢式單例模式,獲得3個對象
Hungry h1 = Hungry.getInstances();
Hungry h2 = Hungry.getInstances();
Hungry h3 = Hungry.getInstances();
//列印其Hash code
System.out.println("h1:" + h1);
System.out.println("h2:" + h2);
System.out.println("h3:" + h3);
//通過懶漢式單例模式,獲得3個對象
Lazybones l1 = Lazybones.getInstances();
Lazybones l2 = Lazybones.getInstances();
Lazybones l3 = Lazybones.getInstances();
//列印其Hash code
System.out.println("l1:" + l1);
System.out.println("l2:" + l2);
System.out.println("l3:" + l3);
}
}
其結果為:
老王說:“從結果,我們可以看到,通過getInstances()
方法得到的對象,無論獲取多少次,的確為同一對象。這裡我再說一下單例模式的關鍵點:1. 私有的構造函數,防止從外部構造對象。2. 靜態的私有變數存儲對象。3.共開的獲取對象的方法。通過這三點,就可以完成單例模式的編寫,通過運用單例模式,我們可以避免重覆構造對象,從而節省系統的記憶體資源消耗。”
小蔡說:“老王,這就是你說的只有一個寶寶的代碼?”
老王說:“對啊。正是。”
小蔡眨了眨眼,又問:“那如果我不止想要一個寶寶呢?現在國家政策放開了,可以要兩個寶寶了。”
老王說:“那你就得先找老公了呀。”
小蔡怒道:“我問的是代碼!只要2個寶寶的代碼!”
老王笑道:“那就是多例模式,我們下回再講。”
更多內容,正在趕來,敬請關註“小蔡和老王的技術日常”,獲取最新最全的內容。。