kapok

          垃圾桶,嘿嘿,我藏的這么深你們還能找到啊,真牛!

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            455 隨筆 :: 0 文章 :: 76 評論 :: 0 Trackbacks
          http://www.uml.org.cn/j2ee/200501241.htm

          單例模式完全剖析(3)---- 探究簡單卻又使人迷惑的單例模式
          kevin 翻譯 選自:java研究組織
          使用注冊表

          使用一個單例類注冊表可以:
          • 在運行期指定單例類
          • 防止產生多個單例類子類的實例
            在例8的單例類中,保持了一個通過類名進行注冊的單例類注冊表:
            例8 帶注冊表的單例類
            1. import java.util.HashMap;
            2. import org.apache.log4j.Logger;
            3.  
            4. public class Singleton {
            5.    private static HashMap map = new HashMap();
            6.    private static Logger logger = Logger.getRootLogger();
            7.  
            8.    protected Singleton() {
            9.       // Exists only to thwart instantiation
            10.    }
            11.    public static synchronized Singleton getInstance(String classname) {
            12.       if(classname == nullthrow new IllegalArgumentException("Illegal classname");
            13.          Singleton singleton = (Singleton)map.get(classname);
            14.  
            15.       if(singleton != null) {
            16.          logger.info("got singleton from map: " + singleton);
            17.          return singleton;
            18.       }
            19.       if(classname.equals("SingeltonSubclass_One"))
            20.             singleton = new SingletonSubclass_One();         
            21.          else if(classname.equals("SingeltonSubclass_Two"))
            22.             singleton = new SingletonSubclass_Two();
            23.  
            24.       map.put(classname, singleton);
            25.       logger.info("created singleton: " + singleton);
            26.       return singleton;
            27.    }
            28.    // Assume functionality follows that's attractive to inherit
            29. }

            這段代碼的基類首先創建出子類的實例,然后把它們存儲在一個Map中。但是基類卻得付出很高的代價因為你必須為每一個子類替換它的getInstance()方法。幸運的是我們可以使用反射處理這個問題。

            使用反射


            在例9的帶注冊表的單例類中,使用反射來實例化一個特殊的類的對象。與例8相對的是通過這種實現,Singleton.getInstance()方法不需要在每個被實現的子類中重寫了。
            例9 使用反射實例化單例類
            1. import java.util.HashMap;
            2. import org.apache.log4j.Logger;
            3.  
            4. public class Singleton {
            5.    private static HashMap map = new HashMap();
            6.    private static Logger logger = Logger.getRootLogger();
            7.  
            8.    protected Singleton() {
            9.       // Exists only to thwart instantiation
            10.    }
            11.    public static synchronized Singleton getInstance(String classname) {
            12.       Singleton singleton = (Singleton)map.get(classname);
            13.  
            14.       if(singleton != null) {
            15.          logger.info("got singleton from map: " + singleton);
            16.          return singleton;
            17.       }
            18.       try {
            19.          singleton = (Singleton)Class.forName(classname).newInstance();
            20.       }
            21.       catch(ClassNotFoundException cnf) {
            22.          logger.fatal("Couldn't find class " + classname);    
            23.       }
            24.       catch(InstantiationException ie) {
            25.          logger.fatal("Couldn't instantiate an object of type " + classname);    
            26.       }
            27.       catch(IllegalAccessException ia) {
            28.          logger.fatal("Couldn't access class " + classname);    
            29.       }
            30.       map.put(classname, singleton);
            31.       logger.info("created singleton: " + singleton);
            32.  
            33.       return singleton;
            34.    }
            35. }


            關于單例類的注冊表應該說明的是:它們應該被封裝在它們自己的類中以便最大限度的進行復用。

            封裝注冊表


            例10列出了一個單例注冊表類。
            例10 一個SingletonRegistry類
            1. import java.util.HashMap;
            2. import org.apache.log4j.Logger;
            3.  
            4. public class SingletonRegistry {
            5.    public static SingletonRegistry REGISTRY = new SingletonRegistry();
            6.  
            7.    private static HashMap map = new HashMap();
            8.    private static Logger logger = Logger.getRootLogger();
            9.  
            10.    protected SingletonRegistry() {
            11.       // Exists to defeat instantiation
            12.    }
            13.    public static synchronized Object getInstance(String classname) {
            14.       Object singleton = map.get(classname);
            15.  
            16.       if(singleton != null) {
            17.          return singleton;
            18.       }
            19.       try {
            20.          singleton = Class.forName(classname).newInstance();
            21.          logger.info("created singleton: " + singleton);
            22.       }
            23.       catch(ClassNotFoundException cnf) {
            24.          logger.fatal("Couldn't find class " + classname);    
            25.       }
            26.       catch(InstantiationException ie) {
            27.          logger.fatal("Couldn't instantiate an object of type " + 
            28.                        classname);    
            29.       }
            30.       catch(IllegalAccessException ia) {
            31.          logger.fatal("Couldn't access class " + classname);    
            32.       }
            33.       map.put(classname, singleton);
            34.       return singleton;
            35.    }
            36. }

            注意我是把SingletonRegistry類作為一個單例模式實現的。我也通用化了這個注冊表以便它能存儲和取回任何類型的對象。例11顯示了的Singleton類使用了這個注冊表。
            例11 使用了一個封裝的注冊表的Singleton類
            1. import java.util.HashMap;
            2. import org.apache.log4j.Logger;
            3.  
            4. public class Singleton {
            5.  
            6.    protected Singleton() {
            7.       // Exists only to thwart instantiation.
            8.    }
            9.    public static Singleton getInstance() {
            10.       return (Singleton)SingletonRegistry.REGISTRY.getInstance(classname);
            11.    }
            12. }

            上面的Singleton類使用那個注冊表的唯一實例通過類名取得單例對象。
            現在我們已經知道如何實現線程安全的單例類和如何使用一個注冊表去在運行期指定單例類名,接著讓我們考查一下如何安排類載入器和處理序列化。

            Classloaders


            在許多情況下,使用多個類載入器是很普通的--包括servlet容器--所以不管你在實現你的單例類時是多么小心你都最終可以得到多個單例類的實例。如果你想要確保你的單例類只被同一個的類載入器裝入,那你就必須自己指定這個類載入器;例如:
            1. private static Class getClass(String classname) 
            2.                                          throws ClassNotFoundException {
            3.       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            4.  
            5.       if(classLoader == null)
            6.          classLoader = Singleton.class.getClassLoader();
            7.  
            8.       return (classLoader.loadClass(classname));
            9.    }
            10. }

            這個方法會嘗試把當前的線程與那個類載入器相關聯;如果classloader為null,這個方法會使用與裝入單例類基類的那個類載入器。這個方法可以用Class.forName()代替。

            序列化


            如果你序列化一個單例類,然后兩次重構它,那么你就會得到那個單例類的兩個實例,除非你實現readResolve()方法,像下面這樣:
            例12 一個可序列化的單例類
            1. import org.apache.log4j.Logger;
            2.  
            3. public class Singleton implements java.io.Serializable {
            4.    public static Singleton INSTANCE = new Singleton();
            5.  
            6.    protected Singleton() {
            7.       // Exists only to thwart instantiation.
            8.    }
            9. [b]      private Object readResolve() {
            10.             return INSTANCE;
            11.       }[/b]}

            上面的單例類實現從readResolve()方法中返回一個唯一的實例;這樣無論Singleton類何時被重構,它都只會返回那個相同的單例類實例。
            例13測試了例12的單例類:
            例13 測試一個可序列化的單例類
            1. import java.io.*;
            2. import org.apache.log4j.Logger;
            3. import junit.framework.Assert;
            4. import junit.framework.TestCase;
            5.  
            6. public class SingletonTest extends TestCase {
            7.    private Singleton sone = null, stwo = null;
            8.    private static Logger logger = Logger.getRootLogger();
            9.  
            10.    public SingletonTest(String name) {
            11.       super(name);
            12.    }
            13.    public void setUp() {
            14.       sone = Singleton.INSTANCE;
            15.       stwo = Singleton.INSTANCE;
            16.    }
            17.    public void testSerialize() {
            18.       logger.info("testing singleton serialization...");
            19. [b]      writeSingleton();
            20.       Singleton s1 = readSingleton();
            21.       Singleton s2 = readSingleton();
            22.       Assert.assertEquals(true, s1 == s2);[/b]   }
            23.    private void writeSingleton() {
            24.       try {
            25.          FileOutputStream fos = new FileOutputStream("serializedSingleton");
            26.          ObjectOutputStream oos = new ObjectOutputStream(fos);
            27.          Singleton s = Singleton.INSTANCE;
            28.  
            29.          oos.writeObject(Singleton.INSTANCE);
            30.          oos.flush();
            31.       }
            32.       catch(NotSerializableException se) {
            33.          logger.fatal("Not Serializable Exception: " + se.getMessage());
            34.       }
            35.       catch(IOException iox) {
            36.          logger.fatal("IO Exception: " + iox.getMessage());
            37.       }
            38.    }
            39.    private Singleton readSingleton() {
            40.       Singleton s = null;
            41.  
            42.       try {
            43.          FileInputStream fis = new FileInputStream("serializedSingleton");
            44.          ObjectInputStream ois = new ObjectInputStream(fis);
            45.          s = (Singleton)ois.readObject();
            46.       }
            47.       catch(ClassNotFoundException cnf) {
            48.          logger.fatal("Class Not Found Exception: " + cnf.getMessage());
            49.       }
            50.       catch(NotSerializableException se) {
            51.          logger.fatal("Not Serializable Exception: " + se.getMessage());
            52.       }
            53.       catch(IOException iox) {
            54.          logger.fatal("IO Exception: " + iox.getMessage());
            55.       }
            56.       return s;
            57.    }
            58.    public void testUnique() {
            59.       logger.info("testing singleton uniqueness...");
            60.       Singleton another = new Singleton();
            61.  
            62.       logger.info("checking singletons for equality");
            63.       Assert.assertEquals(true, sone == stwo);
            64.    }
            65. }

            前面這個測試案例序列化例12中的單例類,并且兩次重構它。然后這個測試案例檢查看是否被重構的單例類實例是同一個對象。下面是測試案例的輸出:
            1.  
            2. Buildfile: build.xml
            3.  
            4. init:
            5.      [echo] Build 20030422 (22-04-2003 11:32)
            6.  
            7. compile:
            8.  
            9. run-test-text:
            10.      [java] .INFO main: testing singleton serialization...
            11.      [java] .INFO main: testing singleton uniqueness...
            12.      [java] INFO main: checking singletons for equality
            13.  
            14.      [java] Time: 0.1
            15.  
            16.      [java] OK (2 tests)

            單例模式結束語


            單例模式簡單卻容易讓人迷惑,特別是對于Java的開發者來說。在這篇文章中,作者演示了Java開發者在顧及多線程、類載入器和序列化情況如何實現單例模式。作者也展示了你怎樣才能實現一個單例類的注冊表,以便能夠在運行期指定單例類。
          posted on 2005-04-18 14:33 笨笨 閱讀(333) 評論(0)  編輯  收藏 所屬分類: J2EEALLJ2SE
          主站蜘蛛池模板: 阜平县| 图木舒克市| 册亨县| 雅江县| 阜平县| 黄山市| 甘洛县| 墨江| 上杭县| 常德市| 齐河县| 禹州市| 宜昌市| 邳州市| 诸城市| 读书| 台中县| 桂林市| 江山市| 洱源县| 怀远县| 邛崃市| 灵石县| 乡城县| 河津市| 隆昌县| 洛川县| 南宫市| 宝山区| 社旗县| 清镇市| 温泉县| 平利县| 五家渠市| 阿荣旗| 沈阳市| 浑源县| 保亭| 博罗县| 胶州市| 新疆|