單例模式(三種實現)


1 餓漢式

public class EagerSingleton {
static {
System.out.println("EagerSingleton 被加載");
}

private EagerSingleton(){}  //私有化構造方法,限制直接構造,只能調用 getInstance() 方法獲取單例對象


private static final EagerSingleton eagerSingleton=new EagerSingleton(); // 私有化靜態 final成員,類加載直接生成單例對象,比較占用內存 
public static EagerSingleton getInstance(){  //提供對外的公共api獲取單例對象
return eagerSingleton;
}

}

總結:餓漢式單例的特點:餓漢式在類創建的同時就實例化一個靜態對象出來,不管之后會不會使用這個單例,都會占據一定的內存,但是相應的,在第一次調用時速度也會更快,因為其資源已經初始化完成。

 

2 懶漢式

public class LazySingleton {
static {
System.out.println("LazySingleton 被加載");
}

private LazySingleton(){} //私有化構造方法,限制直接構造,只能調用 getInstance() 方法獲取單例對象
private static LazySingleton lazySingleton=null;//靜態域初始化為null,為的是需要時再創建,避免像餓漢式那樣占用內存
public static LazySingleton getInstance(){//提供對外的公共api獲取單例對象
if(lazySingleton==null){ 
synchronized (LazySingleton.class){ //在getInstance中做了兩次null檢查,確保了只有第一次調用單例的時候才會做同步,這樣也是線程安全的,同時避免了每次都同步的性能損耗
if(lazySingleton==null){
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}

}

總結:有同步鎖的性能消耗

3 靜態內部類實現

public class IoDHSingleton {
static {
System.out.println("IoDHSingleton 被加載");
}

private IoDHSingleton(){} //私有化構造方法,限制直接構造,只能調用 getInstance() 方法獲取單例對象


public static IoDHSingleton getInstance(){//提供對外的公共api獲取單例對象
return HolderClass.ioDHSingleton; //當getInstance方法第一次被調用的時候,它第一次讀取HolderClass.ioDHSingleton,內部類HolderClass類得到初始化;而這個類在裝載並被初始化的時候,會初始化它的靜態域,從而創建ioDHSingleton 的實例,由於是靜態的域,                                                            因此只會在虛擬機裝載類的時候初始化一次,並由虛擬機來保證它的線程安全性。
}

private static class HolderClass{
static {
System.out.println("HolderClass 被加載");
}
private static IoDHSingleton ioDHSingleton = new IoDHSingleton();
}

 // 防止反序列化獲取多個對象的漏洞  

private Object readResolve() throws ObjectStreamException {    

return  HolderClass.ioDHSingleton;  

    }  


}

這個模式的優勢在於,getInstance方法並沒有被同步,並且只是執行一個域的訪問,因此延遲初始化並沒有增加任何訪問成本。

考慮反射:

 

  由於在調用 SingletonHolder.instance 的時候,才會對單例進行初始化,而且通過反射,是不能從外部類獲取內部類的屬性的。

  所以這種形式,很好的避免了反射入侵。

 

考慮多線程:

 

  由於靜態內部類的特性,只有在其被第一次引用的時候才會被加載,所以可以保證其線程安全性。

 

總結:

  優勢:兼顧了懶漢模式的內存優化(使用時才初始化)以及餓漢模式的安全性(不會被反射入侵)。

  劣勢:需要兩個類去做到這一點,雖然不會創建靜態內部類的對象,但是其 Class 對象還是會被創建,而且是屬於永久帶的對象。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM