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

news/2024/7/4 1:32:01 标签: 设计模式, servlet, resources, 性能优化, java, class
class="baidu_pl">
class="article_content clearfix">
class="htmledit_views">

单例模式适合于一个类只有一个实例的情况,比如窗口管理器,打印缓冲池和文件系统,它们都是原型的例子。典型的情况是,那些对象的类型被遍及一个软件系统的不同对象访问,因此需要一个全局的访问指针,这便是众所周知的单例模式的应用。当然这只有在你确信你不再需要任何多于一个的实例的情况下。
单例模式的用意在于前一段中所关心的。通过单例模式你可以:

  • 确保一个类只有一个实例被建立
  • 提供了一个对对象的全局访问指针
  • 在不影响单例类的客户端的情况下允许将来有多个实例

    尽管单例class="tags" href="/tags/SheJiMoShi.html" title=设计模式>设计模式如在下面的图中的所显示的一样是最简单的class="tags" href="/tags/SheJiMoShi.html" title=设计模式>设计模式,但对于粗心的Java开发者来说却呈现出许多缺陷。这篇文章讨论了单例模式并揭示了那些缺陷。
    注意:你可以从Resources下载这篇文章的源代码。

    单例模式


    在《class="tags" href="/tags/SheJiMoShi.html" title=设计模式>设计模式》一书中,作者这样来叙述单例模式的:确保一个类只有一个实例并提供一个对它的全局访问指针。
    下图说明了单例模式的类图。
    (图1)

    单例模式的类图

    正如你在上图中所看到的,这不是单例模式的完整部分。此图中单例类保持了一个对唯一的单例实例的静态引用,并且会从静态getInstance()方法中返回对那个实例的引用。
    例1显示了一个经典的单例模式的实现。
    例1.经典的单例模式
    public class ClassicSingleton {
    private static ClassicSingleton instance = null;

    protected ClassicSingleton() {
    // Exists only to defeat instantiation.
    }
    public static ClassicSingleton getInstance() {
    if(instance == null) {
    instance = new ClassicSingleton();
    }
    return instance;
    }
    }

    在例1中的单例模式的实现很容易理解。ClassicSingleton类保持了一个对单独的单例实例的静态引用,并且从静态方法getInstance()中返回那个引用。
    关于ClassicSingleton类,有几个让我们感兴趣的地方。首先,ClassicSingleton使用了一个众所周知的懒汉式实例化去创建那个单例类的引用;结果,这个单例类的实例直到getInstance()方法被第一次调用时才被创建。这种技巧可以确保单例类的实例只有在需要时才被建立出来。其次,注意ClassicSingleton实现了一个protected的构造方法,这样客户端不能直接实例化一个ClassicSingleton类的实例。然而,你会惊奇的发现下面的代码完全合法:
    public class SingletonInstantiator { 
    public SingletonInstantiator() {
    ClassicSingleton instance = ClassicSingleton.getInstance();
    ClassicSingleton anotherInstance =
    new ClassicSingleton();
    ...
    }
    }

    前面这个代码片段为何能在没有继承ClassicSingleton并且ClassicSingleton类的构造椒ㄊ莗rotected的情况下创建其实例?答案是protected的构造方法可以被其子类以及在同一个包中的其它类调用。因为ClassicSingleton和SingletonInstantiator位于相同的包(缺省的包),所以SingletonInstantiator方法能创建ClasicSingleton的实例。
    这种情况下有两种解决方案:一是你可以使ClassicSingleton的构造方法变化私有的(private)这样只有ClassicSingleton的方法能调用它;然而这也意味着ClassicSingleton不能有子类。有时这是一种很合意的解决方法,如果确实如此,那声明你的单例类为final是一个好主意,这样意图明确,并且让编译器去使用一些class="tags" href="/tags/XingNengYouHua.html" title=性能优化>性能优化选项。另一种解决方法是把你的单例类放到一个外在的包中,以便在其它包中的类(包括缺省的包)无法实例化一个单例类。
    关于ClassicSingleton的第三点感兴趣的地方是,如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些Servlet容器对每个class="tags" href="/tags/SERVLET.html" title=servlet>servlet使用完全不同的类装载器,这样的话如果有两个class="tags" href="/tags/SERVLET.html" title=servlet>servlet访问一个单例类,它们就都会有各自的实例。
    第四点,如果ClasicSingleton实现了class="tags" href="/tags/JAVA.html" title=java>java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。
    最后也许是最重要的一点,就是例1中的ClassicSingleton类不是线程安全的。如果两个线程,我们称它们为线程1和线程2,在同一时间调用ClassicSingleton.getInstance()方法,如果线程1先进入if块,然后线程2进行控制,那么就会有ClassicSingleton的两个的实例被创建。

    正如你从前面的讨论中所看到的,尽管单例模式是最简单的class="tags" href="/tags/SheJiMoShi.html" title=设计模式>设计模式之一,在Java中实现它也是决非想象的那么简单。这篇文章接下来会揭示Java规范对单例模式进行的考虑,但是首先让我们近水楼台的看看你如何才能测试你的单例类。

http://www.niftyadmin.cn/n/1140360.html

相关文章

简单线性回归——异常值的处理

学习计量经济学过程中,在考虑线性回归中如果因变量中有异常值,应该怎么处理,找到了以下信息,转载备忘一下 1.当发现异常值时,首先应该回到数据中,检查是否存在数据收集或者数据录入方面的错误 如果发现此…

anaconda:一直处于adding featured channel状态

C:\Users\Administrator.condarc 把.condarc内容改成: channels: https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ show_channel_urls: true ssl_verify: true

coroutine协程库源码分析(转)

好久未搬砖,突然想了解下协程的本质,在网上找到这篇文章,讲的不错,搬来存档。 随着Golang的兴起,协程尤其是有栈协程(stackful coroutine)越来越受到程序员的关注。协程几乎成了程序员的一套必备技能。 云风实现了一…

基于栈虚拟机vs基于寄存器虚拟机(转)

今天在网上查了资料,想了解下python虚拟机,收到了一篇是用python时间简单的虚拟机的 ,其中提到了"而 lua 则是基于寄存器的虚拟机",出于好奇遍查找了一下什么是基于寄存器的虚拟机,找到了以下转载文章。 前…

【分层说明】什么是对象?

最近在看Python源码剖析,里边提到了一些关于对象的解释,有了一些灵感,初步从系统架构视角下,分层整理了一下对象的技术理解。 分层图解 面向对象特性 一般大家介绍对象特性时,主要关注的是应用层和语言层的对象特性&…

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

测试单例模式 接下来,我使用与log4j相对应的JUnit来测试单例类,它会贯穿在这篇文章余下的部分。如果你对JUnit或log4j不很熟悉,请参考相关资源。例2是一个用JUnit测试例1的单例模式的案例:例2.一个单例模式的案例import org.apach…

pandas 日期格式

找了好久,详情见官网 https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#timeseries-offset-aliases

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

使用注册表 使用一个单例类注册表可以:在运行期指定单例类防止产生多个单例类子类的实例在例8的单例类中,保持了一个通过类名进行注册的单例类注册表:例8 带注册表的单例类 import java.util.HashMap; import org.apache.log4j.Logger; pub…