建議:用私有構造器或者列舉型別強化Singleton屬性。

孤芳不自賞發表於2017-08-10

        Singleton指僅僅被例項化一次的類。

        Singleton通常被用來代表那些本質上唯一的系統元件,比如視窗管理器或者檔案系統。使類成為Singleton會使它的客戶端測試變得十分困難,因為無法給Singleton替換模擬實現,除非它實現一個充當其型別的介面。

        在Java1.5發行版本之前,實現Singleton有兩種方法。這兩種方法都要把構造器保持為私有的,並匯出共有的靜態成員,以便允許客戶端能夠訪問該類的唯一例項。

        在第一種方法中,共有靜態成員是個final域:

       // Singleton with public final field

      public class Elvis {

          public static final Elvis INSTANCE = new Elvis();

          private Elvis(){...}

          public void leaveTheBuilding(){...}

      }

      私有構造器僅被呼叫一次,用來例項化公有的靜態final域Elvis.INSTANCE。由於缺少公有的或者受保護構造器,所以保證了Elvis的全域性唯一性:一旦Elvis類被例項化,只會存在一個Elvis例項。但要提醒一點:享有特權的客戶端可以藉助AccessibleObject.setAccessible方法,通過反射機制呼叫私有構造器。如果需要抵禦這種攻擊,可以修改構造器,讓它在被要求建立第二個例項的時候丟擲異常。

      在實現Singleton的第二種方法中,公有的成員是個靜態工廠方法:     

 // Singleton with static factory

     public class Elvis {

        private static final Elvis INSTANCE = new Elvis();

        private Elvis() {...}

        public static Elvis getInstance() { return INSTANCE;}

        public void leaveTheBuilding(){...}

    }

     公有域方法的主要好處在於,組成類的成員的宣告很清楚地表明瞭這個類是一個Singleton:公有的靜態域是final的,所以該域將總是包含相同的物件引用。公有域方法在效能上不再有任何優勢:現在的JVM實現幾乎都能夠將靜態工廠發的呼叫內聯化。

      工廠方法的優勢之一在於,它提供了靈活性:在不改變其API的前提下,我們可以改變該類是否應該為Singleton的想法。工廠方法返回該類的唯一例項,但是,他可以很容易被修改,比如改成為每個呼叫該方法的執行緒返回一個唯一的例項。第二個優勢與泛型有關。

      為了使利用這其中一種方法的Singleton類變成可序列化的(Serializable),僅僅在宣告彙總加上“implements Serializable”是不夠的。為了維護並保證Singleton,必須宣告所有的例項域都是瞬時(transient)的,並提供一個readResolve方法。否則每次反序列化一個序列化的例項時,都會建立一個新的例項。在我們的例子中,會導致“假冒的Elvis”。為了防止這種情況,要在Elvis勒種加入下面這個readResolve方法:  

   // readResolve method to preserve singleton property

      private Object readResolve() {

         // Return the one true Elvis and let the garbage collector

         // take care of the Elvis impersonator.

         return INSTANCE;

     }

     從Java1.5發行版本起,實現Singleton還有第三種方法。只需編寫一個包含單個元素的列舉型別:
   

 // Enum singleton - the preferred approach

     public enum Elvis {

      INSTANCE;

      public void leaveTheBuilding() {...}

    }

     這種方法在功能上與公有域相似,但是它更加簡潔,無償的提供了序列化機制,絕對防止多次例項化,即使是在面對複雜的序列化或者反射攻擊的時候。雖然這種方法還沒有廣泛採用,但是單元素的列舉型別已經成為實現Singleton的最佳方法。

相關文章