I want to fly higher
          programming Explorer
          posts - 114,comments - 263,trackbacks - 0
          第三條:用私有構(gòu)造器或者枚舉類型強(qiáng)化Singleton屬性
             
               1.Singleton指僅僅被實(shí)例化一次的類。Singleton通常被用來(lái)代表那些本質(zhì)上唯一的系統(tǒng)組件,如窗口管理器或者文件系統(tǒng)。使類稱為Singleton會(huì)使它的客戶端調(diào)試變的十分困難,因?yàn)闊o(wú)法給Singleton替換模擬實(shí)現(xiàn),除非它實(shí)現(xiàn)一個(gè)充當(dāng)其類型的接口.
               2.在Java1.5發(fā)行版本之前,實(shí)現(xiàn)Singleton有兩種方法。這兩種方法都要把構(gòu)造器保持為私有,并導(dǎo)出公有的靜態(tài)成員,以便客戶端能夠訪問(wèn)該類的唯一實(shí)例,以便允許client能夠訪問(wèn)該類的唯一實(shí)例。
               3.第一種方法中,公有靜態(tài)成員是個(gè)final域:
                public class Singleton1
             {
              public static final Singleton1 INSTANCE = new Singleton1();
              //私有改造函數(shù)
              private Singleton1()
              {
              }
              //其他方法實(shí)現(xiàn)
              public void otherMethod()
              {
               //...
              }
             }
             私有構(gòu)造器僅被調(diào)用一次,用來(lái)實(shí)例化公有的靜態(tài)final域Singleton1.INSTANCE.由于缺少公有的或者受保護(hù)的構(gòu)造器,所以保證了Singleton1的全局唯一性。一旦其被實(shí)例化,只會(huì)存在一個(gè)實(shí)例,不多也不少??蛻舳说娜魏涡袨槎疾粫?huì)改變這一點(diǎn)。
                 :享有特權(quán)的client可以借助AccessibleObject.setAccessible方法,通過(guò)反射機(jī)制調(diào)用私有構(gòu)造器。如果要抵御這種攻擊,需要修改構(gòu)造器,讓其在創(chuàng)建第二個(gè)實(shí)例的時(shí)候拋出異常。
                4.在實(shí)現(xiàn)Singleton的第二種方法中,公有的成員是個(gè)靜態(tài)工廠方法:
             public class Singleton2
             {
              //私有static Instance
              private static final Singleton2 INSTANCE = new Singleton2();
              
              //私有構(gòu)造函數(shù)
              private Singleton2()
              {
              }
              
              //獲取單例方法
              public static Singleton2 getInstance()
              {
               return INSTANCE;
              }
              
              //其他方法
              public void otherMethod()
              {
               //...
              }
             }
             對(duì)于靜態(tài)方法Singleton2.getInstance的所有調(diào)用,都會(huì)返回同一個(gè)對(duì)象引用,所以永遠(yuǎn)不會(huì)創(chuàng)建其他的實(shí)例。
             :上述利用反射的提醒依然適用。
                5.公有域方法的好處在于,組成類的成員的聲明很清楚的表明了這個(gè)類是一個(gè)Singleton(final),公有的靜態(tài)域是final的,所以該域?qū)⒖偸前嗤膶?duì)象引用。公有域方法在性能上不再有任何優(yōu)勢(shì):現(xiàn)在JVM實(shí)現(xiàn)幾乎都能都將靜態(tài)工廠方法的調(diào)用內(nèi)聯(lián)化。
                注:內(nèi)聯(lián):指函數(shù)在被調(diào)用的地方直接展開,編譯器在調(diào)用時(shí)不用像一般函數(shù)那樣,參數(shù)壓棧,返回時(shí)參數(shù)出棧以及資源釋放等,這樣提高了程序執(zhí)行速度.
                6.工廠方法的優(yōu)勢(shì)在于,它提供了靈活性:在不改變其API的前提下,我們可以改變?cè)擃愂欠駷镾ingleton的想法。工廠方法返回該類的唯一實(shí)例,不過(guò)它可以很容易被修改,如改成為每個(gè)調(diào)用該方法的線程返回一個(gè)唯一的實(shí)例。
                 第二個(gè)優(yōu)勢(shì)在于與泛型有關(guān)。
                 這些優(yōu)勢(shì)之間通常不相關(guān),public域的方法比較簡(jiǎn)單.
                7.以上的兩種的其中一種方法實(shí)現(xiàn)的Singleton類如果變成是可序列化的Serialiazble,僅僅在聲明加上implements Serializable是不夠的。為了維護(hù)并保證Singleton,必須聲明所有實(shí)例都是瞬時(shí)transient的,并提供 一個(gè)readResolve方法。否則每次反序列化一個(gè)序列化的實(shí)例時(shí),都會(huì)創(chuàng)建一個(gè)新的實(shí)例。即會(huì)導(dǎo)致一個(gè)假冒的對(duì)象。為了防止這種情況,需要在單例類中增加readResolve方法->
                 注:readResolve方法用來(lái)重新指定反序列化得到的對(duì)象.
             private Object readResolve()
             {
              return INSTANCE;
             }
                8.從Java 1.5發(fā)行版本起,實(shí)現(xiàn)Singleton還有第三種方法。只需編寫一個(gè)包含單個(gè)元素的枚舉類型。
            public Enum Singleton3
            {
             INSTANCE;
             
             public void otherMethod()
             {
             }
            }
                這種方法在功能上與公有域方法相近,但是其更加簡(jiǎn)潔,無(wú)償提供了序列化機(jī)制,絕對(duì)防止多次實(shí)例化,即使是面對(duì)復(fù)雜的序列化或者反射攻擊的時(shí)候。 雖然這種方法還沒有廣泛使用,但是單元素的枚舉類型已經(jīng)成為實(shí)現(xiàn)Singleton的最佳方法。
                注:用反射調(diào)用私有構(gòu)造函數(shù):報(bào)錯(cuò):
                 Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects.
                 ps:枚舉真心不錯(cuò).

          部分源碼:
          package com.book.chap2.singleton;

          import java.lang.reflect.Constructor;

          /**
           * 
           * 單例實(shí)現(xiàn)1
           * <p>
           * 公有靜態(tài)成員是個(gè)public final域
           * <p>
           * 為防止client利用反射調(diào)用私有改造函數(shù),所以在創(chuàng)建第二個(gè)實(shí)例的時(shí)候拋出了異常
           * 
           * @author landon
           * @since 1.6.0_35
           * @version 1.0.0 2013-1-9
           * 
           
          */


          public class Singleton1
          {
              
          public static final Singleton1 instance = new Singleton1();

              
          // 私有改造函數(shù)
              private Singleton1()
              
          {
                  
          if (instance != null)
                  
          {
                      
          throw new IllegalArgumentException("No exist the second instance");
                  }

              }


              
          // 其他方法實(shí)現(xiàn)
              public void otherMethod()
              
          {
                  
          // 
                  System.out.println("call otherMethod");
              }


              @SuppressWarnings("rawtypes")
              
          public static void main(String args) throws Exception
              
          {
                  Singleton1 singleton = Singleton1.instance;
                  singleton.otherMethod();

                  
          // 利用反射調(diào)用私有構(gòu)造器
                  Constructor[] arrayConstructor = singleton.getClass()
                          .getDeclaredConstructors();
                  
          for (Constructor constructor : arrayConstructor)
                  
          {
                      
          // 調(diào)用setAccessible(true);
                      constructor.setAccessible(true);

                      
          // 實(shí)例化,這里一定會(huì)拋出異常
                      constructor.newInstance();
                  }

              }

          }


          package com.book.chap2.singleton;

          import java.io.Serializable;

          /**
          *
          *單例實(shí)現(xiàn)2
          *<p>公有的成員為靜態(tài)工廠方法
          *<p>序列化時(shí),要實(shí)現(xiàn)readResolve方法,防止反序列化出新的實(shí)例
          *
          *@author landon
          *@since 1.6.0_35
          *@version 1.0.0 2013-1-9
          *
          */


          public class Singleton2 implements Serializable
          {
             
          //私有static Instance
              private static final Singleton2 INSTANCE = new Singleton2();
             
             
          //私有構(gòu)造函數(shù)
              private Singleton2()
             
          {
              }

             
             
          //獲取單例方法
              public static Singleton2 getInstance()
             
          {
                 
          return INSTANCE;
              }

             
             
          //其他方法
              public void otherMethod()
             
          {
                 
          //
              }

             
             
          //必須提供該方法,以便重新指定反序列化得到的對(duì)象.
              private Object readResolve()
             
          {
                 
          return INSTANCE;
              }

          }


          package com.book.chap2.singleton;

          import java.lang.reflect.Constructor;

          /**
          *
          *枚舉實(shí)現(xiàn)單例
          *<p>目前最好的方式,避免了反射的攻擊和序列化的問(wèn)題
          *
          *<pre>
          *反射調(diào)用枚舉私有構(gòu)造函數(shù)測(cè)試結(jié)果:
          *    Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
              at java.lang.reflect.Constructor.newInstance(Unknown Source)
              at com.book.chap2.singleton.Singleton3.main(Singleton3.java:34)
          *</pre>
          *
          *@author landon
          *@since 1.6.0_35
          *@version 1.0.0 2013-1-9
          *
          */


          public enum Singleton3 {
              INSTANCE;
             
             
          public void otherMethod()
             
          {
              }

             
             
          public static void main(Stringargs) throws Exception
             
          {
                 
          //測(cè)試,是否可以反射生成枚舉
                 
          // 利用反射調(diào)用私有構(gòu)造器
                  Constructor[] arrayConstructor = Singleton3.INSTANCE.getClass().ge        tDeclaredConstructors();
          for (Constructor constructor : arrayConstructor)
                 
          {
                     
          // 調(diào)用setAccessible(true);
                      constructor.setAccessible(true);

                     
          // 實(shí)例化,這里一定會(huì)拋出異常
                      constructor.newInstance();
                  }

              }


          }

          posted on 2013-03-15 15:38 landon 閱讀(2511) 評(píng)論(2)  編輯  收藏 所屬分類: Program 、Book

          FeedBack:
          # re: Effective Java 2nd筆記3:用私有構(gòu)造器或者枚舉類型強(qiáng)化Singleton屬性
          2013-03-16 10:49 | 背道而馳
          注:享有特權(quán)的client可以借助AccessibleObject.setAccessible方法,通過(guò)反射機(jī)制調(diào)用私有構(gòu)造器。如果要抵御這種攻擊,需要修改構(gòu)造器,讓其在創(chuàng)建第二個(gè)實(shí)例的時(shí)候拋出異常。

          請(qǐng)問(wèn)樓主啊,如果判斷是第二次創(chuàng)建實(shí)例?  回復(fù)  更多評(píng)論
            
          # re: Effective Java 2nd筆記3:用私有構(gòu)造器或者枚舉類型強(qiáng)化Singleton屬性
          2013-03-16 11:08 | 背道而馳
          剛才看了后面曉得了,謝謝樓主的分享!!  回復(fù)  更多評(píng)論
            
          主站蜘蛛池模板: 海安县| 江山市| 滦南县| 武鸣县| 嫩江县| 金华市| 宜州市| 阿拉善左旗| 陆河县| 遵化市| 商河县| 翁源县| 秦安县| 五峰| 通榆县| 镇远县| 长顺县| 井研县| 宕昌县| 沙田区| 托克逊县| 如东县| 兴安盟| 手游| 大邑县| 昭平县| 漳州市| 奇台县| 乐至县| 禹城市| 汉沽区| 财经| 韶关市| 天镇县| 通城县| 陈巴尔虎旗| 通化市| 谷城县| 陇南市| 沁水县| 张家口市|