設計模式第六天之單例模式

這次和大家分享一下設計模式中的單例模式。

說到單例模式,我相信大家都瞭解,簡單說就是某個類在整個程序中只有一個對象。那爲什麼使用單例模式呢?什麼場景使用單例模式呢?

我個人認爲,在程序設計中,經過分析,某個類有一個對象已經可以滿足要求,如果此時再加上這個類會消耗許多資源(包括內存開銷大,創建對象耗時等),這個類就推薦使用單例來實現了。比如:數據庫連接及相關操作,圖片加載,緩存,線程池等等。使用單例模式也可以更好的實現共享資源。

那如何實現單例模式呢?在這裏我和大家分享五種方式。在這五種方式中,都是以一個簡單的計算器例子來說明,計算加減乘除的類對象明顯在程序中只要有一個就可以了。在這裏只是爲了介紹這些方式實現。

1.      餓漢模式

/**
 * 餓漢模式實現單例模式
 * 好處:簡單
 * 缺點:
 * 當類加載的時候就創建了對象而不管對象時候在使用
 * 造成資源的浪費
 * 當內存不足的時候,回收了靜態對象,此時訪問也會帶來bug
 * @author wangpeiyu
 *
 */
public class Hungry {
	private static Hungry instance= new Hungry();
	
	private Hungry(){
		
	}
	public Hungry getInstance(){
		return instance;
	}
	public int add(int a,int b){
		return a+b;
	}
	public int sub(int a,int b){
		return a-b;
	}
	public int mul(int a, int b){
		return a*b;
	}
	public float div(int a,int b)
	{
		return a/b;
	}
}

2.      懶漢模式

/**
 * 懶漢實現單例模式
 * 好處:解決了線程安全,也做到了要使用的時候才創建對象
 * 缺點:每次要獲取該對象的時候,都要進行線程同步,即使在instance已經賦值了之後
 * 還要進行線程同步,這就造成了不必要的時間浪費和資源浪費
 * @author wangpeiyu
 *
 */
public class Lazy {
	private static Lazy instance=null;
	private  Lazy(){}
	public synchronized static Lazy getInstance(){
		if(instance==null){
			instance = new Lazy();
		}
		return instance;
	}
	public int add(int a,int b){
		return a+b;
	}
	public int sub(int a,int b){
		return a-b;
	}
	public int mul(int a, int b){
		return a*b;
	}
	public float div(int a,int b)
	{
		return a/b;
	}
}


3.      靜態內部類方式

/**
 * 靜態內部類實現單例模式
 * 好處:線程安全、也達到了延遲加載、當加載單例類的時候並沒有創建對象。
 * 缺點:內存不足,回收靜態對象時,在訪問會出現bug
 * 無法解決發序列化導致的對象重建
 * @author wangpeiyu
 *
 */
public class StaticInClass {
	
	private StaticInClass(){
		
	}
	public static StaticInClass getInstance()
	{
		return SingletonHolder.instance;
	}
	/**
	 * 靜態內部類
	 * 主要的單例類的holder
	 * @author wangpeiyu
	 *
	 */
	private static final class SingletonHolder{
		private static final StaticInClass instance = new StaticInClass();
	}
	public int add(int a,int b){
		return a+b;
	}
	public int sub(int a,int b){
		return a-b;
	}
	public int mul(int a, int b){
		return a*b;
	}
	public float div(int a,int b)
	{
		return a/b;
	}
}


4.      雙重檢查加鎖方式

/**
 * 雙重檢查加鎖模式實現單例模式
 * 好處:延遲了加載、線程安全,而且也避免了在instance已經賦值的情況下,多線程還要
 * 同步造成的資源浪費和無意義的等待
 * 缺點:當反序列化時無法避免會重新創建對象的問題
 * @author wangpeiyu
 *
 */
public class DCL {
	private static DCL instance = null;
	private DCL(){}
	public static DCL getInstance(){
		if(instance==null){
			synchronized (DCL.class) {
				if(instance==null)
				{
					instance = new DCL();
				}
			}
		}
		return instance;
	}
	public int add(int a,int b){
		return a+b;
	}
	public int sub(int a,int b){
		return a-b;
	}
	public int mul(int a, int b){
		return a*b;
	}
	public float div(int a,int b)
	{
		return a/b;
	}
}



5.      枚舉方法

/**
 * 使用枚舉實現單例
 * 好處:
 * 1.完全確保整個程序只有一個實例
 * 2.線程安全,因爲枚舉一旦定義結束,編譯器就不允許我們再使用其構造器來創建任何實例了
 * 3.構造函數是private的,即使定義爲public,一樣爲private
 * 4.自由序列化,這樣即使在反序列化時也不會重新創建
 * @author wangpeiyu
 *
 */
public enum EnumCaculate {
	instance;
	private EnumCaculate(){
	}
	public int add(int a,int b){
		return a+b;
	}
	public int sub(int a,int b){
		return a-b;
	}
	public int mul(int a, int b){
		return a*b;
	}
	public float div(int a,int b)
	{
		return a/b;
	}
}


    在這五種實現中,我個人比較推薦枚舉實現和雙重檢查加鎖實現。原因在每個類的代碼中體現了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章