kapok

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

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            455 隨筆 :: 0 文章 :: 76 評(píng)論 :: 0 Trackbacks
          http://www.uml.org.cn/j2ee/200501241.htm

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

          使用一個(gè)單例類(lèi)注冊(cè)表可以:
          • 在運(yùn)行期指定單例類(lèi)
          • 防止產(chǎn)生多個(gè)單例類(lèi)子類(lèi)的實(shí)例
            在例8的單例類(lèi)中,保持了一個(gè)通過(guò)類(lèi)名進(jìn)行注冊(cè)的單例類(lèi)注冊(cè)表:
            例8 帶注冊(cè)表的單例類(lèi)
            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. }

            這段代碼的基類(lèi)首先創(chuàng)建出子類(lèi)的實(shí)例,然后把它們存儲(chǔ)在一個(gè)Map中。但是基類(lèi)卻得付出很高的代價(jià)因?yàn)槟惚仨殲槊恳粋€(gè)子類(lèi)替換它的getInstance()方法。幸運(yùn)的是我們可以使用反射處理這個(gè)問(wèn)題。

            使用反射


            在例9的帶注冊(cè)表的單例類(lèi)中,使用反射來(lái)實(shí)例化一個(gè)特殊的類(lèi)的對(duì)象。與例8相對(duì)的是通過(guò)這種實(shí)現(xiàn),Singleton.getInstance()方法不需要在每個(gè)被實(shí)現(xiàn)的子類(lèi)中重寫(xiě)了。
            例9 使用反射實(shí)例化單例類(lèi)
            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. }


            關(guān)于單例類(lèi)的注冊(cè)表應(yīng)該說(shuō)明的是:它們應(yīng)該被封裝在它們自己的類(lèi)中以便最大限度的進(jìn)行復(fù)用。

            封裝注冊(cè)表


            例10列出了一個(gè)單例注冊(cè)表類(lèi)。
            例10 一個(gè)SingletonRegistry類(lèi)
            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類(lèi)作為一個(gè)單例模式實(shí)現(xiàn)的。我也通用化了這個(gè)注冊(cè)表以便它能存儲(chǔ)和取回任何類(lèi)型的對(duì)象。例11顯示了的Singleton類(lèi)使用了這個(gè)注冊(cè)表。
            例11 使用了一個(gè)封裝的注冊(cè)表的Singleton類(lèi)
            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類(lèi)使用那個(gè)注冊(cè)表的唯一實(shí)例通過(guò)類(lèi)名取得單例對(duì)象。
            現(xiàn)在我們已經(jīng)知道如何實(shí)現(xiàn)線(xiàn)程安全的單例類(lèi)和如何使用一個(gè)注冊(cè)表去在運(yùn)行期指定單例類(lèi)名,接著讓我們考查一下如何安排類(lèi)載入器和處理序列化。

            Classloaders


            在許多情況下,使用多個(gè)類(lèi)載入器是很普通的--包括servlet容器--所以不管你在實(shí)現(xiàn)你的單例類(lèi)時(shí)是多么小心你都最終可以得到多個(gè)單例類(lèi)的實(shí)例。如果你想要確保你的單例類(lèi)只被同一個(gè)的類(lèi)載入器裝入,那你就必須自己指定這個(gè)類(lèi)載入器;例如:
            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. }

            這個(gè)方法會(huì)嘗試把當(dāng)前的線(xiàn)程與那個(gè)類(lèi)載入器相關(guān)聯(lián);如果classloader為null,這個(gè)方法會(huì)使用與裝入單例類(lèi)基類(lèi)的那個(gè)類(lèi)載入器。這個(gè)方法可以用Class.forName()代替。

            序列化


            如果你序列化一個(gè)單例類(lèi),然后兩次重構(gòu)它,那么你就會(huì)得到那個(gè)單例類(lèi)的兩個(gè)實(shí)例,除非你實(shí)現(xiàn)readResolve()方法,像下面這樣:
            例12 一個(gè)可序列化的單例類(lèi)
            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]}

            上面的單例類(lèi)實(shí)現(xiàn)從readResolve()方法中返回一個(gè)唯一的實(shí)例;這樣無(wú)論Singleton類(lèi)何時(shí)被重構(gòu),它都只會(huì)返回那個(gè)相同的單例類(lèi)實(shí)例。
            例13測(cè)試了例12的單例類(lèi):
            例13 測(cè)試一個(gè)可序列化的單例類(lèi)
            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. }

            前面這個(gè)測(cè)試案例序列化例12中的單例類(lèi),并且兩次重構(gòu)它。然后這個(gè)測(cè)試案例檢查看是否被重構(gòu)的單例類(lèi)實(shí)例是同一個(gè)對(duì)象。下面是測(cè)試案例的輸出:
            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)

            單例模式結(jié)束語(yǔ)


            單例模式簡(jiǎn)單卻容易讓人迷惑,特別是對(duì)于Java的開(kāi)發(fā)者來(lái)說(shuō)。在這篇文章中,作者演示了Java開(kāi)發(fā)者在顧及多線(xiàn)程、類(lèi)載入器和序列化情況如何實(shí)現(xiàn)單例模式。作者也展示了你怎樣才能實(shí)現(xiàn)一個(gè)單例類(lèi)的注冊(cè)表,以便能夠在運(yùn)行期指定單例類(lèi)。
          posted on 2005-04-18 14:33 笨笨 閱讀(336) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): J2EEALLJ2SE
          主站蜘蛛池模板: 全南县| 丹巴县| 松溪县| 桐梓县| 乌恰县| 红原县| 益阳市| 铁岭县| 施甸县| 景宁| 连山| 大宁县| 济宁市| 拉萨市| 甘南县| 容城县| 宿州市| 陵川县| 安康市| 吉林市| 花莲市| 台北市| 高碑店市| 米泉市| 洪雅县| 嵩明县| 凤台县| 成安县| 五指山市| 南康市| 耿马| 黄山市| 乐都县| 绥中县| 宝丰县| 三亚市| 天全县| 广安市| 甘泉县| 桑日县| 黑水县|