爲什麼叫做餓漢式,意思是很飢餓,那麼就會一開始就準備好,以防以後吃不飽,名字由此而來。代碼以下java
class HUNGRYMAN{ //這裏實例化方法要設置成私有的,以防外部直接new對象,破壞單例 private HUNGRYMAN(){ } //這裏即爲一開始就建立好對象,須要調用的時候,直接返回,不須要新建立 private static HUNGRYMAN INSTANCE = new HUNGRYMAN(); //調用方法返回實例對象 public static HUNGRYMAN getInstance(){ return INSTANCE; } } class Main{ public static void main(String[] args) { //咱們在這裏採用哈希值的方式來判斷是否相等 HUNGRYMAN instance1 = HUNGRYMAN.getInstance(); HUNGRYMAN instance2 = HUNGRYMAN.getInstance(); System.out.println("instance1的哈希值爲" + instance1.hashCode() + " " + "instance2的哈希值爲" + instance2.hashCode()); System.out.println("二者是否相等:" + (instance1 == instance2)); } } //輸出:instance1的哈希值爲366712642 instance2的哈希值爲366712642 //二者是否相等:true
因爲上述的狀況,誕生了懶漢式:數組
與餓漢式相對,懶漢式只有在須要使用的時候纔會建立,也就是說能夠實現「延時建立」,代碼以下:安全
class LAZYMAN{ //一樣,這裏的實例方法也須要設置成私有的,防止其餘類破壞單例 private LAZYMAN(){ } //這裏定義一個LAZYMAN類型的變量instance,可是並無建立對象 private static LAZYMAN INSTANCE; //在執行getInstance方法的時候纔會建立對象 public static LAZYMAN getInstance(){ //判空時初始化,不然直接返回對象 if(INSTANCE == null){ INSTANCE = new LAZYMAN(); } return INSTANCE; } } class Main{ public static void main(String[] args) { LAZYMAN instance1 = LAZYMAN.getInstance(); LAZYMAN instance2 = LAZYMAN.getInstance(); System.out.println("instance1的哈希值爲" + instance1.hashCode() + " " + "instance2的哈希值爲" + instance2.hashCode()); System.out.println("二者是否相等:" + (instance1 == instance2)); } } //輸出:instance1的哈希值爲366712642 instance2的哈希值爲366712642 //輸出:二者是否相等:true
由此產生了雙重校驗鎖(DCL懶漢式)的單例模式實現方式併發
線程安全的懶漢模式,直接來看代碼:線程
class LAZYMAN{ private LAZYMAN(){ } //這裏加入volatile,是爲了防止實例對象建立的時候出現指令重排 /* 對象建立過程(非原子性):1.分配內存空間 2.執行構造方法,初始化對象 3.將對象指向這個內存空間。這裏若是沒有使用volatile修飾的話,會致使 執行順序不是123,多是132,致使對象建立尚未徹底建立完畢,可是外部 執行到if(instance == null)這裏的時候,已經不爲空了,會直接返回,致使 返回版初始化狀態的實例,發生錯誤。 */ private static volatile LAZYMAN INSTANCE; //在執行getInstance方法的時候纔會建立對象 public static LAZYMAN getInstance(){ //若是對象沒有建立過,先把整個class鎖住,等到第一個拿到鎖的線程釋放以後其餘的線程在繼續執行 if(INSTANCE == null){ synchronized(LAZYMAN.class){ if(INSTANCE == null){ INSTANCE = new LAZYMAN(); } } } return INSTANCE; } } class Main{ public static void main(String[] args) { LAZYMAN instance1 = LAZYMAN.getInstance(); LAZYMAN instance2 = LAZYMAN.getInstance(); System.out.println("instance1的哈希值爲" + instance1.hashCode() + " " + "instance2的哈希值爲" + instance2.hashCode()); System.out.println("二者是否相等:" + (instance1 == instance2)); } }
關於 volatile 和 synchronized ,在另外的文章中會探討。
這裏說一下如何經過反射來破壞:code
class Main{ public static void main(String[] args) throws Exception { LAZYMAN instance1 = LAZYMAN.getInstance(); Constructor<LAZYMAN> constructor = LAZYMAN.class.getDeclaredConstructor(); //無視私有構造器 constructor.setAccessible(true); //直接建立 LAZYMAN instance2 = constructor.newInstance(); System.out.println("instance1的哈希值爲" + instance1.hashCode() + " " + "instance2的哈希值爲" + instance2.hashCode()); System.out.println("二者是否相等:" + (instance1 == instance2)); } } //輸出:instance1的哈希值爲366712642 instance2的哈希值爲1829164700 //二者是否相等:false
class INNERSINGLE{ private INNERSINGLE(){ } //內部類,調用的時候才進行加載 private static class SINGLEINNNER{ private static INNERSINGLE instance = new INNERSINGLE(); } //調用方法:getInstance() public static INNERSINGLE getInstance(){ return SINGLEINNNER.instance; } } class Main{ public static void main(String[] args) throws Exception { INNERSINGLE instance1 = INNERSINGLE.getInstance(); INNERSINGLE instance2 = INNERSINGLE.getInstance(); System.out.println("instance1的哈希值爲" + instance1.hashCode() + " " + "instance2的哈希值爲" + instance2.hashCode()); System.out.println("二者是否相等:" + (instance1 == instance2)); } } //輸出:instance1的哈希值爲366712642 instance2的哈希值爲366712642 //二者是否相等:true
枚舉類默認是單例模式的對象
public enum ENUMSINGLE{ INSTANCE; public ENUMSINGLE getInstance(){ return INSTANCE; } } class Main{ public static void main(String[] args) throws Exception { ENUMSINGLE instance1 = ENUMSINGLE.INSTANCE; ENUMSINGLE instance2 = ENUMSINGLE.INSTANCE; System.out.println("instance1的哈希值爲" + instance1.hashCode() + " " + "instance2的哈希值爲" + instance2.hashCode()); System.out.println("二者是否相等:" + (instance1 == instance2)); }