单例模式完全剖析.doc
《单例模式完全剖析.doc》由会员分享,可在线阅读,更多相关《单例模式完全剖析.doc(30页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、单例模式关键字: 设计模式 一、什么是单例模式? 在我们的Java应用程序中,随着应用程序的运行会创建出很多对象。但有时候我们希望在创建某类对象时,无论创建多少次该类对象只有一份在内存中。这就是单例模式。如果我们要实现单例模式首先要保证构造函数私有化,即不允许用户随意调用我本类的构造函数,因为只要通过new操作创建对象,必定要在堆中开辟空间,这样就无法实现单例了。 二、单例模式的实现 public class Singleton private static Singleton single=null; private Singleton() public static Singleton g
2、etSingleton() if(single=null) single=new Singleton(); return single; 概要 单例模式是最简单的设计模式之一,但是对于Java的开发者来说,它却有很多缺陷。在本月的专栏中,David Geary探讨了单例模式以及在面对多线程(multithreading)、类装载器(classloaders)和序列化(serialization)时如何处理这些缺陷。 单例模式适合于一个类只有一个实例的情况,比如窗口管理器,打印缓冲池和文件系统,它们都是原型的例子。典型的情况是,那些对象的类型被遍及一个软件系统的不同对象访问,因此需要一个全局的访
3、问指针,这便是众所周知的单例模式的应用。当然这只有在你确信你不再需要任何多于一个的实例的情况下。 单例模式的用意在于前一段中所关心的。通过单例模式你可以: 确保一个类只有一个实例被建立 提供了一个对对象的全局访问指针 在不影响单例类的客户端的情况下允许将来有多个实例 尽管单例设计模式如在下面的图中的所显示的一样是最简单的设计模式,但对于粗心的Java开发者来说却呈现出许多缺陷。这篇文章讨论了单例模式并揭示了那些缺陷。 注意:你可以从Resources下载这篇文章的源代码。 单例模式 在设计模式一书中,作者这样来叙述单例模式的:确保一个类只有一个实例并提供一个对它的全局访问指针。 下图说明了单例
4、模式的类图。 (图1) 单例模式的类图 正如你在上图中所看到的,这不是单例模式的完整部分。此图中单例类保持了一个对唯一的单例实例的静态引用,并且会从静态getInstance()方法中返回对那个实例的引用。 例1显示了一个经典的单例模式的实现。 例1.经典的单例模式 Java代码 1. publicclassClassicSingleton 2. privatestaticClassicSingletoninstance=null; 3. 4. protectedClassicSingleton() 5. /Existsonlytodefeatinstantiation. 6. 7. publ
5、icstaticClassicSingletongetInstance() 8. if(instance=null) 9. instance=newClassicSingleton(); 10. 11. returninstance; 12. 13. public class ClassicSingleton private static ClassicSingleton instance = null; protected ClassicSingleton() / Exists only to defeat instantiation. public static ClassicSingle
6、ton getInstance() if(instance = null) instance = new ClassicSingleton(); return instance; 在例1中的单例模式的实现很容易理解。ClassicSingleton类保持了一个对单独的单例实例的静态引用,并且从静态方法getInstance()中返回那个引用。 关于ClassicSingleton类,有几个让我们感兴趣的地方。首先,ClassicSingleton使用了一个众所周知的懒汉式实例化去创建那个单例类的引用;结果,这个单例类的实例直到getInstance()方法被第一次调用时才被创建。这种技巧可以确
7、保单例类的实例只有在需要时才被建立出来。其次,注意ClassicSingleton实现了一个protected的构造方法,这样客户端不能直接实例化一个ClassicSingleton类的实例。然而,你会惊奇的发现下面的代码完全合法: Java代码 1. publicclassSingletonInstantiator 2. publicSingletonInstantiator() 3. ClassicSingletoninstance=ClassicSingleton.getInstance(); 4. ClassicSingletonanotherInstance= 5. newClass
8、icSingleton(); 6. . 7. 8. public class SingletonInstantiator public SingletonInstantiator() ClassicSingleton instance = ClassicSingleton.getInstance(); ClassicSingleton anotherInstance = new ClassicSingleton(); . 前面这个代码片段为何能在没有继承ClassicSingleton并且ClassicSingleton类的构造方法是protected的情况下创建其实例?答案是protecte
9、d的构造方法可以被其子类以及在同一个包中的其它类调用。因为ClassicSingleton和SingletonInstantiator位于相同的包(缺省的包),所以SingletonInstantiator方法能创建ClasicSingleton的实例。 这种情况下有两种解决方案:一是你可以使ClassicSingleton的构造方法变化私有的(private)这样只有ClassicSingleton的方法能调用它;然而这也意味着ClassicSingleton不能有子类。有时这是一种很合意的解决方法,如果确实如此,那声明你的单例类为final是一个好主意,这样意图明确,并且让编译器去使用一些
10、性能优化选项。另一种解决方法是把你的单例类放到一个外在的包中,以便在其它包中的类(包括缺省的包)无法实例化一个单例类。 关于ClassicSingleton的第三点感兴趣的地方是,如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。 第四点,如果ClasicSingleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,
11、那你就会有多个单例类的实例。 最后也许是最重要的一点,就是例1中的ClassicSingleton类不是线程安全的。如果两个线程,我们称它们为线程1和线程2,在同一时间调用ClassicSingleton.getInstance()方法,如果线程1先进入if块,然后线程2进行控制,那么就会有ClassicSingleton的两个的实例被创建。 正如你从前面的讨论中所看到的,尽管单例模式是最简单的设计模式之一,在Java中实现它也是决非想象的那么简单。这篇文章接下来会揭示Java规范对单例模式进行的考虑,但是首先让我们近水楼台的看看你如何才能测试你的单例类。 测试单例模式 接下来,我使用与log
12、4j相对应的JUnit来测试单例类,它会贯穿在这篇文章余下的部分。如果你对JUnit或log4j不很熟悉,请参考相关资源。 例2是一个用JUnit测试例1的单例模式的案例: 例2.一个单例模式的案例 Java代码 1. importorg.apache.log4j.Logger; 2. importjunit.framework.Assert; 3. importjunit.framework.TestCase; 4. 5. publicclassSingletonTestextendsTestCase 6. privateClassicSingletonsone=null,stwo=null
13、; 7. privatestaticLoggerlogger=Logger.getRootLogger(); 8. 9. publicSingletonTest(Stringname) 10. super(name); 11. 12. publicvoidsetUp() 13. logger.info(gettingsingleton.); 14. sone=ClassicSingleton.getInstance(); 15. logger.info(.gotsingleton:+sone); 16. 17. logger.info(gettingsingleton.); 18. stwo=
14、ClassicSingleton.getInstance(); 19. logger.info(.gotsingleton:+stwo); 20. 21. publicvoidtestUnique() 22. logger.info(checkingsingletonsforequality); 23. Assert.assertEquals(true,sone=stwo); 24. 25. import org.apache.log4j.Logger; import junit.framework.Assert; import junit.framework.TestCase; public
15、 class SingletonTest extends TestCase private ClassicSingleton sone = null, stwo = null; private static Logger logger = Logger.getRootLogger(); public SingletonTest(String name) super(name); public void setUp() logger.info(getting singleton.); sone = ClassicSingleton.getInstance(); logger.info(.got
16、singleton: + sone); logger.info(getting singleton.); stwo = ClassicSingleton.getInstance(); logger.info(.got singleton: + stwo); public void testUnique() logger.info(checking singletons for equality); Assert.assertEquals(true, sone = stwo); 例2两次调用ClassicSingleton.getInstance(),并且把返回的引用存储在成员变量中。方法tes
17、tUnique()会检查这些引用看它们是否相同。例3是这个测试案例的输出: 例3.是这个测试案例的输出 Java代码 1. Buildfile:build.xml 2. 3. init: 4. echoBuild20030414(14-04-200303:08) 5. 6. compile: 7. 8. run-test-text: 9. java.INFOmain:bgettingsingleton./b 10. javaINFOmain:bcreatedsingleton:/bSingletone86f41 11. javaINFOmain:.gotsingleton:Singletone
18、86f41 12. javaINFOmain:bgettingsingleton./b 13. javaINFOmain:.gotsingleton:Singletone86f41 14. javaINFOmain:checkingsingletonsforequality 15. 16. javaTime:0.032 17. 18. javaOK(1test)Buildfile: build.xml init: echo Build 20030414 (14-04-2003 03:08) compile: run-test-text: java .INFO main: bgetting si
19、ngleton./b java INFO main: bcreated singleton:/b Singletone86f41 java INFO main: .got singleton: Singletone86f41 java INFO main: bgetting singleton./b java INFO main: .got singleton: Singletone86f41 java INFO main: checking singletons for equality java Time: 0.032 java OK (1 test)正如前面的清单所示,例2的简单测试顺利
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 模式 完全 剖析
限制150内