`
izuoyan
  • 浏览: 8947167 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

单例模式完全剖析(3)---- 探究简单却又使人迷惑的单例模式

阅读更多

使用注册表


使用一个单例类注册表可以:
  • 在运行期指定单例类
  • 防止产生多个单例类子类的实例
    在例8的单例类中,保持了一个通过类名进行注册的单例类注册表:
    例8带注册表的单例类
    1. importjava.util.HashMap;
    2. importorg.apache.log4j.Logger;
    3. publicclassSingleton{
    4. privatestaticHashMapmap=newHashMap();
    5. privatestaticLoggerlogger=Logger.getRootLogger();
    6. protectedSingleton(){
    7. //Existsonlytothwartinstantiation
    8. }
    9. publicstaticsynchronizedSingletongetInstance(Stringclassname){
    10. if(classname==null)thrownewIllegalArgumentException("Illegalclassname");
    11. Singletonsingleton=(Singleton)map.get(classname);
    12. if(singleton!=null){
    13. logger.info("gotsingletonfrommap:"+singleton);
    14. returnsingleton;
    15. }
    16. if(classname.equals("SingeltonSubclass_One"))
    17. singleton=newSingletonSubclass_One();
    18. elseif(classname.equals("SingeltonSubclass_Two"))
    19. singleton=newSingletonSubclass_Two();
    20. map.put(classname,singleton);
    21. logger.info("createdsingleton:"+singleton);
    22. returnsingleton;
    23. }
    24. //Assumefunctionalityfollowsthat'sattractivetoinherit
    25. }

    这段代码的基类首先创建出子类的实例,然后把它们存储在一个Map中。但是基类却得付出很高的代价因为你必须为每一个子类替换它的getInstance()方法。幸运的是我们可以使用反射处理这个问题。

    使用反射


    在例9的带注册表的单例类中,使用反射来实例化一个特殊的类的对象。与例8相对的是通过这种实现,Singleton.getInstance()方法不需要在每个被实现的子类中重写了。
    例9使用反射实例化单例类
    1. importjava.util.HashMap;
    2. importorg.apache.log4j.Logger;
    3. publicclassSingleton{
    4. privatestaticHashMapmap=newHashMap();
    5. privatestaticLoggerlogger=Logger.getRootLogger();
    6. protectedSingleton(){
    7. //Existsonlytothwartinstantiation
    8. }
    9. publicstaticsynchronizedSingletongetInstance(Stringclassname){
    10. Singletonsingleton=(Singleton)map.get(classname);
    11. if(singleton!=null){
    12. logger.info("gotsingletonfrommap:"+singleton);
    13. returnsingleton;
    14. }
    15. try{
    16. singleton=(Singleton)Class.forName(classname).newInstance();
    17. }
    18. catch(ClassNotFoundExceptioncnf){
    19. logger.fatal("Couldn'tfindclass"+classname);
    20. }
    21. catch(InstantiationExceptionie){
    22. logger.fatal("Couldn'tinstantiateanobjectoftype"+classname);
    23. }
    24. catch(IllegalAccessExceptionia){
    25. logger.fatal("Couldn'taccessclass"+classname);
    26. }
    27. map.put(classname,singleton);
    28. logger.info("createdsingleton:"+singleton);
    29. returnsingleton;
    30. }
    31. }


    关于单例类的注册表应该说明的是:它们应该被封装在它们自己的类中以便最大限度的进行复用。

    封装注册表


    例10列出了一个单例注册表类。
    例10一个SingletonRegistry类
    1. importjava.util.HashMap;
    2. importorg.apache.log4j.Logger;
    3. publicclassSingletonRegistry{
    4. publicstaticSingletonRegistryREGISTRY=newSingletonRegistry();
    5. privatestaticHashMapmap=newHashMap();
    6. privatestaticLoggerlogger=Logger.getRootLogger();
    7. protectedSingletonRegistry(){
    8. //Existstodefeatinstantiation
    9. }
    10. publicstaticsynchronizedObjectgetInstance(Stringclassname){
    11. Objectsingleton=map.get(classname);
    12. if(singleton!=null){
    13. returnsingleton;
    14. }
    15. try{
    16. singleton=Class.forName(classname).newInstance();
    17. logger.info("createdsingleton:"+singleton);
    18. }
    19. catch(ClassNotFoundExceptioncnf){
    20. logger.fatal("Couldn'tfindclass"+classname);
    21. }
    22. catch(InstantiationExceptionie){
    23. logger.fatal("Couldn'tinstantiateanobjectoftype"+
    24. classname);
    25. }
    26. catch(IllegalAccessExceptionia){
    27. logger.fatal("Couldn'taccessclass"+classname);
    28. }
    29. map.put(classname,singleton);
    30. returnsingleton;
    31. }
    32. }

    注意我是把SingletonRegistry类作为一个单例模式实现的。我也通用化了这个注册表以便它能存储和取回任何类型的对象。例11显示了的Singleton类使用了这个注册表。
    例11使用了一个封装的注册表的Singleton类
    1. importjava.util.HashMap;
    2. importorg.apache.log4j.Logger;
    3. publicclassSingleton{
    4. protectedSingleton(){
    5. //Existsonlytothwartinstantiation.
    6. }
    7. publicstaticSingletongetInstance(){
    8. return(Singleton)SingletonRegistry.REGISTRY.getInstance(classname);
    9. }
    10. }

    上面的Singleton类使用那个注册表的唯一实例通过类名取得单例对象。
    现在我们已经知道如何实现线程安全的单例类和如何使用一个注册表去在运行期指定单例类名,接着让我们考查一下如何安排类载入器和处理序列化。

    Classloaders


    在许多情况下,使用多个类载入器是很普通的--包括servlet容器--所以不管你在实现你的单例类时是多么小心你都最终可以得到多个单例类的实例。如果你想要确保你的单例类只被同一个的类载入器装入,那你就必须自己指定这个类载入器;例如:
    1. privatestaticClassgetClass(Stringclassname)
    2. throwsClassNotFoundException{
    3. ClassLoaderclassLoader=Thread.currentThread().getContextClassLoader();
    4. if(classLoader==null)
    5. classLoader=Singleton.class.getClassLoader();
    6. return(classLoader.loadClass(classname));
    7. }
    8. }

    这个方法会尝试把当前的线程与那个类载入器相关联;如果classloader为null,这个方法会使用与装入单例类基类的那个类载入器。这个方法可以用Class.forName()代替。

    序列化


    如果你序列化一个单例类,然后两次重构它,那么你就会得到那个单例类的两个实例,除非你实现readResolve()方法,像下面这样:
    例12一个可序列化的单例类
    1. importorg.apache.log4j.Logger;
    2. publicclassSingletonimplementsjava.io.Serializable{
    3. publicstaticSingletonINSTANCE=newSingleton();
    4. protectedSingleton(){
    5. //Existsonlytothwartinstantiation.
    6. }
    7. [b]privateObjectreadResolve(){
    8. returnINSTANCE;
    9. }[/b]}

    上面的单例类实现从readResolve()方法中返回一个唯一的实例;这样无论Singleton类何时被重构,它都只会返回那个相同的单例类实例。
    例13测试了例12的单例类:
    例13测试一个可序列化的单例类
    1. importjava.io.*;
    2. importorg.apache.log4j.Logger;
    3. importjunit.framework.Assert;
    4. importjunit.framework.TestCase;
    5. publicclassSingletonTestextendsTestCase{
    6. privateSingletonsone=null,stwo=null;
    7. privatestaticLoggerlogger=Logger.getRootLogger();
    8. publicSingletonTest(Stringname){
    9. super(name);
    10. }
    11. publicvoidsetUp(){
    12. sone=Singleton.INSTANCE;
    13. stwo=Singleton.INSTANCE;
    14. }
    15. publicvoidtestSerialize(){
    16. logger.info("testingsingletonserialization...");
    17. [b]writeSingleton();
    18. Singletons1=readSingleton();
    19. Singletons2=readSingleton();
    20. Assert.assertEquals(true,s1==s2);[/b]}
    21. privatevoidwriteSingleton(){
    22. try{
    23. FileOutputStreamfos=newFileOutputStream("serializedSingleton");
    24. ObjectOutputStreamoos=newObjectOutputStream(fos);
    25. Singletons=Singleton.INSTANCE;
    26. oos.writeObject(Singleton.INSTANCE);
    27. oos.flush();
    28. }
    29. catch(NotSerializableExceptionse){
    30. logger.fatal("NotSerializableException:"+se.getMessage());
    31. }
    32. catch(IOExceptioniox){
    33. logger.fatal("IOException:"+iox.getMessage());
    34. }
    35. }
    36. privateSingletonreadSingleton(){
    37. Singletons=null;
    38. try{
    39. FileInputStreamfis=newFileInputStream("serializedSingleton");
    40. ObjectInputStreamois=newObjectInputStream(fis);
    41. s=(Singleton)ois.readObject();
    42. }
    43. catch(ClassNotFoundExceptioncnf){
    44. logger.fatal("ClassNotFoundException:"+cnf.getMessage());
    45. }
    46. catch(NotSerializableExceptionse){
    47. logger.fatal("NotSerializableException:"+se.getMessage());
    48. }
    49. catch(IOExceptioniox){
    50. logger.fatal("IOException:"+iox.getMessage());
    51. }
    52. returns;
    53. }
    54. publicvoidtestUnique(){
    55. logger.info("testingsingletonuniqueness...");
    56. Singletonanother=newSingleton();
    57. logger.info("checkingsingletonsforequality");
    58. Assert.assertEquals(true,sone==stwo);
    59. }
    60. }

    前面这个测试案例序列化例12中的单例类,并且两次重构它。然后这个测试案例检查看是否被重构的单例类实例是同一个对象。下面是测试案例的输出:
    1. Buildfile:build.xml
    2. init:
    3. [echo]Build20030422(22-04-200311:32)
    4. compile:
    5. run-test-text:
    6. [java].INFOmain:testingsingletonserialization...
    7. [java].INFOmain:testingsingletonuniqueness...
    8. [java]INFOmain:checkingsingletonsforequality
    9. [java]Time:0.1
    10. [java]OK(2tests)

    单例模式结束语


    单例模式简单却容易让人迷惑,特别是对于Java的开发者来说。在这篇文章中,作者演示了Java开发者在顾及多线程、类载入器和序列化情况如何实现单例模式。作者也展示了你怎样才能实现一个单例类的注册表,以便能够在运行期指定单例类。
  • 分享到:
    评论

    相关推荐

      java单例模式完全剖析

      单例模式是最简单的设计模式之一,但是对于Java的开发者来说,它却有很多缺陷。在本月的专栏中,David Geary探讨了单例模式以及在面对多线程(multithreading)、类装载器(classloaders)和序列化(serialization)时...

      单例模式的多种实现.docx

      单例模式的七种实现方法以及分析,可以作文大作业提交 1.前言 4 1.1 课题的研究背景 4 1.2 课题主要研究目标 4 2.相关技术简介 4 2.1Java简介 4 2.2IDEA简介 4 3. 单例模式的7种实现方式 5 3.1饿汉式(使用静态常量...

      单例模式详解.pdf

      1、掌握单例模式的应用场景。 2、掌握 IDEA 环境下的多...3、掌握保证线程安全的单例模式策略。 4、掌握反射暴力攻击单例解决方案及原理分析。 5、序列化破坏单例的原理及解决方案。 6、掌握常见的单例模式写法。

      Java设计模式之单例模式的七种写法

      此文档为Tom老师的公开课的单例的7种写法的一个文档,充分分析单例模式,值得对设计模式有研究的童鞋下下来好好看看

      常见设计模式-单例模式

      单例模式,完整介绍单例模式的几种创建方式 以及对比优缺点,引用spring 源码简单分析 框架如何保证单例

      Java单例模式应用研究.pdf

      单例模式是一种最简单的创建型设计模式,主要用于对系统资源的管理与控制,在软件开发中大量使用,如Windows的文件系统、回收站、打印机等。文中通过内容管理系统项目,深入剖析了几种常用的单例类,包括饿汉式单例类、...

      论文研究-设计模式的研究以及单例模式的应用与实现 .pdf

      设计模式的研究以及单例模式的应用与实现,王晓满 ,张展华,本文介绍了设计模式在软件设计中的重要作用和意义,然后给出了设计模式的定义和基本要素,在此基础之上分析了怎样使用设计模式来

      C++中的单例模式及按需释放模型

      单例模式是设计模式中最简单最容易理解的模式之一,实用方便,项目设计开发中会被经常使用,但是不知道读者有没有考虑过这个问题,单例模式实例什么时候被释放,读者有兴趣可以仔细分析下,按照目前通常方法实现的...

      JavaScript设计模式之单例模式.md

      为了帮助大家快速和较好地理解JavaScript设计模式中的单例模式,本文对JavaScript的单例模式进行了分析并进行简易的代码演示,希望本文能够给有需要的人带来一点小小的帮助。

      php基于单例模式封装mysql类.zip

      介绍了php基于单例模式封装mysql类,结合完整实例形式分析了php使用单例模式封装的mysql类定义与使用方法 掌握满足单例模式的必要条件 (1)私有的构造方法-为了防止在类外使用new关键字实例化...

      php基于单例模式封装mysql类

      介绍了php基于单例模式封装mysql类,结合完整实例形式分析了php使用单例模式封装的mysql类定义与使用方法 掌握满足单例模式的必要条件 (1)私有的构造方法-为了防止在类外使用new关键字实例化对象 (2)私有的成员属性...

      单例设计模式源码和案例解析

      单例设计模式源码和案例解析,详细分析四种单例设计模式的使用方法, 并附有博客文档说明。单例设计模式源码和案例解析

      php设计模式之单例模式实例分析_.docx

      php设计模式之单例模式实例分析_.docx

      单例模式分析及代码

      这是关于单例模式的两种经典版本分析以及代码实现,以及附带了C++中双重检查技术来实现同步访问问题,单例模式在校园招聘的时候也是面试的经典题

      php设计模式之单例模式实例分析

      简单的说,一个对象(在学习设计模式之前,需要比较了解面向对象思想)只负责一个特定的任务; 单例类: 1、构造函数需要标记为private(访问控制:防止外部代码使用new操作符创建对象),单例类不能在其他类中实例...

      java单例模式看这一篇就够了

      深入分析java单例模式什么是单例模式单例模式的常见写法一、饿汉式单例优点缺点示例二、懒汉式单例示例1(普通写法)示例2(synchronized写法)示例3(DCL写法)示例4(内部类写法)三、注册式单例示例1(容器式)示例2(枚举式...

      我国电子商务行业不同商业模式的比较分析--以京东商城与淘宝网作比较.pdf

      我国电子商务行业不同商业模式的比较分析--以京东商城与淘宝网作比较.pdf我国电子商务行业不同商业模式的比较分析--以京东商城与淘宝网作比较.pdf我国电子商务行业不同商业模式的比较分析--以京东商城与淘宝网作比较...

      举例讲解C#编程中对设计模式中的单例模式的运用

      为了帮助大家更好地理解单例模式,大家可以结合下面的类图来进行理解,以及后面也会剖析单例模式的实现思路: 为什么会有单例模式 看完单例模式的介绍,自然大家都会有这样一个疑问——为什么要有单例模式的?它在什么...

      JavaScript设计模式---单例模式详解【四种基本形式】

      主要介绍了JavaScript设计模式---单例模式,结合实例形式详细分析了JavaScript设模式中单例模式的四种基本形式定义与使用方法,需要的朋友可以参考下

      项目中用到的jdbc连接 单例模式

      * 假设线程1进入到步骤2,执行步骤3未完成,会先将instance设为非null值。 * 这时候线程2会在判断instance==null的时候失败,返回一个不完整的intance对象。 * * 尝试最佳实现方法。在该方法中,Singleton有一...

    Global site tag (gtag.js) - Google Analytics