单例类详解

来源:互联网 发布:淘宝家装e站国民包 编辑:程序博客网 时间:2024/06/06 07:23

和寝室的一哥们对最近的状态用一个词总结下“疲于奔命”

 

笔试、面试了不少,都不甚满意,一直想找机会整理下常考的东西

 

单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。

 

单例模式的要点
 
 一是某各类只能有一个实例;

    二是它必须自行创建这个事例;

    三是它必须自行向整个系统提供这个实例。

在下面的对象图中,有一个"单例对象",而"客户甲"、"客户乙" 和"客户丙"是单例对象的三个客户对象。可以看到,所有的客户对象共享一个单例对象。而且从单例对象到自身的连接线可以看出,单例对象持有对自己的引用。

 

  由于Java 语言的特点,使得单例模式在Java 语言的实现上有自己的特点。这些特点主要表现在单例类如何将自己实例化上。

 

 

 


  饿汉式单例类饿汉式单例类是在Java 语言里实现得最为简便的单例类,下面所示的类图描述了一个饿汉式单例类的典型实现。
 饿汉式单例类

 

 

  可以看出,在这个类被加载时,静态变量m_instance 会被初始化,此时类的私有构造方法会被调用。这时候,单例类的惟一实例就被创建出来了。


  Java 语言中单例类的一个最重要的特点是类的构造方法是私有的,从而避免外界利用构造方法直接创建出任意多的实例。值得指出的是,由于构造方法是私有的,因此,此类不能被继承。


  懒汉式单例类
  与饿汉式单例类相同之处是,类的构造方法是私有的。与饿汉式单例类不同的是,懒汉式单例类在第一次被引用时将自己实例化。如果加载器是静态的,那么在懒汉式单例类被加载时不会将自己实例化。

 

 

懒汉式单例类

 

  读者可能会注意到,在上面给出懒汉式单例类实现里对静态工厂方法使用了同步化,以处理多线程环境。

  同样,由于构造方法是私有的,因此,此类不能被继承。饿汉式单例类在自己被加载时就将自己实例化。即便加载器是静态的,在饿汉式单例类被加载时仍会将自己实例化。单从资源利用效率角度来讲,这个比懒汉式单例类稍差些。
  从速度和反应时间角度来讲,则比懒汉式单例类稍好些。然而,懒汉式单例类在实例化时, 必须处理好在多个线程同时首次引用此类时的访问限制问题,特别是当单例类作为资源控制器,在实例化时必然涉及资源初始化,而资源初始化很有可能耗费时间。这意味着出现多线程同时首次引用此类的机率变得较大。
  饿汉式单例类可以在Java 语言内实现, 但不易在C++ 内实现,因为静态初始化在C++ 里没有固定的顺序,因而静态的m_instance 变量的初始化与类的加载顺序没有保证,可能会出问题。这就是为什么GoF 在提出单例类的概念时,举的例子是懒汉式的。他们的书影响之大,以致Java 语言中单例类的例子也大多是懒汉式的。实际上,本书认为饿汉式单例类更符合Java 语言本身的特点。


  登记式单例类
  登记式单例类是GoF 为了克服饿汉式单例类及懒汉式单例类均不可继承的缺点而设计的。  代码清单3

 

 

登记式单例类
  

 

它的子类RegSingletonChild 需要父类的帮助才能实例化。

  

 

 
  在GoF 原始的例子中,并没有getInstance() 方法,这样得到子类必须调用的getInstance(String name)方法并传入子类的名字,因此很不方便。本章在登记式单例类子类的例子里,加入了getInstance() 方法,这样做的好处是RegSingletonChild 可以通过这个方法,返还自已的实例。而这样做的缺点是,由于数据类型不同,无法在RegSingleton 提供这样一个方法。由于子类必须允许父类以构造子调用产生实例,因此,它的构造方法必须是公开的。这样一来,就等于允许了以这样方式产生实例而不在父类的登记中。这是登记式单例类的一个缺点。
  GoF 曾指出,由于父类的实例必须存在才可能有子类的实例,这在有些情况下是一个浪费。这是登记式单例类的另一个缺点。

原创粉丝点击