架構設計之設計模式 (四) Java中多種方式實現單例模式

 
  1. 簡介
    1. 「單例」即單一實例從名字上望文生義便可知道該類是作什麼的,可見設計模式的名字也是很重要的,讓人經過名字就能知道模式的用途,通用性強咱們再命名本身的模式、函數、過程等時候也要遵循這一命名原則,這也成爲了編程中一個不成文的規定。
    1. GOF是這樣定義的:確保某個類只有一個實例,並且自行實例化並向整個系統提供這個實例。
  1. 特色
    1. 有狀態
      1. 一個單例對象能夠是有狀態的(Stateful),一個有狀態的單例對象一般也是可變對象(mutable)
      1. 一個有狀態的單例對象能夠做爲狀態庫(repositary),好比一個單例對象擁有Int類型的屬性,那麼它能夠提供惟一序列號,供系統使用。
      1. 有狀態的單例對象纔有可能出現進程同步問題,這有點像函數的傳值和傳引用,傳值不會改變傳入參數的值固然就沒有影響,只是當一個工具函數來使用。
    1. 無狀態
      1. 另外一方面單例也能夠是沒有狀態(stateless),僅看成工具性函數來使用,既然看成工具函數來使用固然也就沒有必要再建立多個實例,有一個就夠用了,使用單例建立對象很合適。
    1. 不徹底
      1. 構造函數是公開的,正常狀況下設置構造函數不公開是爲了防止外部類對其進行實例化,若是構造函數公開外部類也不必定會調用,說明它也是一個單例,咱們就稱這樣的單例爲不徹底的單例。
    1. JVM
    1. 一個單例只在一個JVM中運行,當兩個類加載器加載同一個類時可能出現兩個單例實例。
  1. 舉例
    1. 在計算機裏面不論是硬件管理仍是軟件都有不少用到單例的地方,好比硬件打印機、端口、傳真口等在某一個特定時刻只容許一個訪問者進行通訊,不然會出現問題
    1. 軟件方面回收站、軟件系統配置文件的讀取都是利用單例實現,由於一個系統中只有一個回收站,回收站本身提供這個實例,
  1. 餓漢與懶漢
    1. 餓漢式
      1. package com.singleton;
        /**
         * Only once instance of the class may be created during the
         * execution of any given program. Instances of this class should
         * be aquired through the getInstance() method. Notice that there
         * are no public constructors for this class.
         */
        public class HungrySingleton {
        		/**
            * @label Creates 
            */
            private static HungrySingleton m_instance = new HungrySingleton();
        	
            private HungrySingleton() { }
        
            public static synchronized  HungrySingleton getInstance()
            {
        	    return m_instance;
        	}
        }

      1. 優缺點
        1. 餓漢單例類在類一加載時就會建立單例對象,並一直存在於內存當中佔用了不少存儲空間,浪費內存,優勢是沒有枷鎖,執行效率會高一些。
    1. 懶漢式
      1. package com.singleton;
        /**
         * Only once instance of the class may be created during the
         * execution of any given program. Instances of this class should
         * be aquired through the getInstance() method. Notice that there
         * are no public constructors for this class.
         */
        public class lazySingleton {
        		/**
            * @label Creates 
            */
            private static lazySingleton m_instance = null;
        	
            private lazySingleton() { }
        
            public static synchronized   lazySingleton getInstance()
            {
        	    if (m_instance == null)
        	    {
        	        m_instance = new lazySingleton();
        	    }
        	    return m_instance;
        	}
        }

      1. 優缺點
        1. 當對象第一次被引用時建立,比餓漢實例化晚一些所以節省資源,可是懶漢會引發線程不安全,須要枷鎖,下降執行效率,。
    1. 那麼有沒有什麼辦法克服兩個模式的缺點呢?
      1. 在《Java編程思想》中詳細介紹了內部類的使用,若是看一看內部類就會知道如何解決這一個問題,內部類是在外部類被調用時才加載,根據這個特色咱們能夠把單例中一開始就須要加載的代碼移動到內部類裏面,這樣它的加載順序就會改變,固然,也就不會再外部類第一次加載時就就行實例化。把餓漢中的初始化代碼寫在內部類裏面,以下:
      1. package com.singleton;
        /**
         * 採用內部類的方式實現單例
         * @author LLS
         *
         */
        public class RegisterSingleton {
        	/*
        	 * 私有構造方法,防止被外類實例化
        	 */
        	private RegisterSingleton(){}
        	/*
        	 * 公開方法
        	 */
        	public static RegisterSingleton getInstance()
        	{
        		return Holder.instance;
        	}
        	/*
        	 * 私有內部類
        	 */
        	private static class Holder
        	{
        		private static final RegisterSingleton instance=new RegisterSingleton();
        	}
        }

      1. 從網上查了查原來這個模式叫作登記式單例,登記式單例的初衷是爲了克服單例不能繼承的缺點,但這樣寫彷佛仍是不可以繼承,想了想這也算是繼承吧。
        1. 由於引入了內部類,內部類能夠實現繼承類的全部功能,咱們也能夠調用內部類,這也至關於繼承類的實現。
        1. 登記式單例(採用繼承)
          1. 這種登記類經過在父類中Register來實現,即把實例化好的對象放入Map(Key,Value),鍵值對分別表明類名、類對應的對象。
          2. 在子類中從Map中取出實例,不過,沒感受到這種繼承有多大好處,若是想繼承父類構造函數最少也是protected級別,那麼子類呢,得是public或者protected,這樣一來子類就能夠經過外部new來實例化,沒有多大意義了。
  1. Java中應用
    1. 在咱們目前作的系統中餓漢式單利已經能解決問題,建議用餓漢式單例模式。或者使用內部類的單例來管理對象建立。
    1. J2EE中單例模式說簡單即簡單說難即難,若是涉及到多個JVM以及加載器問題,單例會比較複雜一些,只是目前尚未碰到過這種狀況。
相關文章
相關標籤/搜索