登记式单例类
登记式单例类是GoF 为了克服饿汉式单例类及懒汉式单例类均不可继承的缺点而设计的。本书把他们的例子翻译为Java 语言,并将它自己实例化的方式从懒汉式改为饿汉式。只是它的子类实例化的方式只能是懒汉式的, 这是无法改变的。如下图所示是登记式单例类的一个例子,图中的关系线表明,此类已将自己实例化。
![]()
代码清单3:登记式单例类
import java.util.HashMap;public class RegSingleton { static private HashMap m_registry = new HashMap();static { RegSingleton x = new RegSingleton();m_registry.put( x.getClass()。getName() , x);} /** * 保护的默认构造子*/ protected RegSingleton() {} /** * 静态工厂方法,返还此类惟一的实例*/ static public RegSingleton getInstance(String name)
{ if (name == null)
{ name = "com.javapatterns.singleton.demos.RegSingleton";} if (m_registry.get(name) == null)
{ try { m_registry.put( name,Class.forName(name)。newInstance() ) ;} catch(Exception e)
{ System.out.println("Error happened.");} return (RegSingleton) (m_registry.get(name) );} /** * 一个示意性的商业方法*/ public String about()
{ return "Hello, I am RegSingleton.";}它的子类RegSingletonChild 需要父类的帮助才能实例化。下图所示是登记式单例类子类的一个例子。图中的关系表明,此类是由父类将子类实例化的。
![]()
下面是子类的源代码。
代码清单4:登记式单例类的子类
import java.util.HashMap;public class RegSingletonChild extends RegSingleton { public RegSingletonChild() {} /** * 静态工厂方法*/ static public RegSingletonChild getInstance()
{ return (RegSingletonChild)
RegSingleton.getInstance("com.javapatterns.singleton.demos.RegSingletonChild" );} /** * 一个示意性的商业方法*/ public String about()
{ return "Hello, I am RegSingletonChild.";}
在GoF 原始的例子中,并没有getInstance() 方法,这样得到子类必须调用的getInstance(String name)方法并传入子类的名字,因此很不方便。本章在登记式单例类子类的例子里,加入了getInstance() 方法,这样做的好处是RegSingletonChild 可以通过这个方法,返还自已的实例。而这样做的缺点是,由于数据类型不同,无法在RegSingleton 提供这样一个方法。由于子类必须允许父类以构造子调用产生实例,因此,它的构造子必须是公开的。这样一来,就等于允许了以这样方式产生实例而不在父类的登记中。这是登记式单例类的一个缺点。
GoF 曾指出,由于父类的实例必须存在才可能有子类的实例,这在有些情况下是一个浪费。这是登记式单例类的另一个缺点。
登记式单例类
登记式单例类是GoF 为了克服饿汉式单例类及懒汉式单例类均不可继承的缺点而设计的。本书把他们的例子翻译为Java 语言,并将它自己实例化的方式从懒汉式改为饿汉式。只是它的子类实例化的方式只能是懒汉式的, 这是无法改变的。如下图所示是登记式单例类的一个例子,图中的关系线表明,此类已将自己实例化。
代码清单3:登记式单例类
import java.util.HashMap;public class RegSingleton { static private HashMap m_registry = new HashMap();static { RegSingleton x = new RegSingleton();m_registry.put( x.getClass()。getName() , x);} /** * 保护的默认构造子*/ protected RegSingleton() {} /** * 静态工厂方法,返还此类惟一的实例*/ static public RegSingleton getInstance(String name)
{ if (name == null)
{ name = "com.javapatterns.singleton.demos.RegSingleton";} if (m_registry.get(name) == null)
{ try { m_registry.put( name,Class.forName(name)。newInstance() ) ;} catch(Exception e)
{ System.out.println("Error happened.");} return (RegSingleton) (m_registry.get(name) );} /** * 一个示意性的商业方法*/ public String about()
{ return "Hello, I am RegSingleton.";}它的子类RegSingletonChild 需要父类的帮助才能实例化。下图所示是登记式单例类子类的一个例子。图中的关系表明,此类是由父类将子类实例化的。
下面是子类的源代码。
代码清单4:登记式单例类的子类
import java.util.HashMap;public class RegSingletonChild extends RegSingleton { public RegSingletonChild() {} /** * 静态工厂方法*/ static public RegSingletonChild getInstance()
{ return (RegSingletonChild)
RegSingleton.getInstance("com.javapatterns.singleton.demos.RegSingletonChild" );} /** * 一个示意性的商业方法*/ public String about()
{ return "Hello, I am RegSingletonChild.";}
在GoF 原始的例子中,并没有getInstance() 方法,这样得到子类必须调用的getInstance(String name)方法并传入子类的名字,因此很不方便。本章在登记式单例类子类的例子里,加入了getInstance() 方法,这样做的好处是RegSingletonChild 可以通过这个方法,返还自已的实例。而这样做的缺点是,由于数据类型不同,无法在RegSingleton 提供这样一个方法。由于子类必须允许父类以构造子调用产生实例,因此,它的构造子必须是公开的。这样一来,就等于允许了以这样方式产生实例而不在父类的登记中。这是登记式单例类的一个缺点。
GoF 曾指出,由于父类的实例必须存在才可能有子类的实例,这在有些情况下是一个浪费。这是登记式单例类的另一个缺点。
在什么情况下使用单例模式
使用单例模式的条件
使用单例模式有一个很重要的必要条件:
在一个系统要求一个类只有一个实例时才应当使用单例模式。反过来说,如果一个类可以有几个实例共存,那么就没有必要使用单例类。但是有经验的读者可能会看到很多不当地使用单例模式的例子,可见做到上面这一点并不容易,下面就是一些这样的情况。
例子一
问:我的一个系统需要一些"全程"变量。学习了单例模式后,我发现可以使用一个单例类盛放所有的"全程"变量。请问这样做对吗?
答:这样做是违背单例模式的用意的。单例模式只应当在有真正的"单一实例"的需求时才可使用。
一个设计得当的系统不应当有所谓的"全程"变量,这些变量应当放到它们所描述的实体所对应的类中去。将这些变量从它们所描述的实体类中抽出来, 放到一个不相干的单例类中去,会使得这些变量产生错误的依赖关系和耦合关系。
例子二
问:我的一个系统需要管理与数据库的连接。学习了单例模式后,我发现可以使用一个单例类包装一个Connection 对象,并在finalize()方法中关闭这个Connection 对象。这样的话,在这个单例类的实例没有被人引用时,这个finalize() 对象就会被调用,因此,Connection 对象就会被释放。这多妙啊。
答:这样做是不恰当的。除非有单一实例的需求,不然不要使用单例模式。在这里Connection 对象可以同时有几个实例共存,不需要是单一实例。
单例模式有很多的错误使用案例都与此例子相似,它们都是试图使用单例模式管理共享资源的生命周期,这是不恰当的。
单例类的状态
有状态的单例类
一个单例类可以是有状态的(stateful),一个有状态的单例对象一般也是可变(mutable) 单例对象。
有状态的可变的单例对象常常当做状态库(repositary)使用。比如一个单例对象可以持有一个int 类型的属性,用来给一个系统提供一个数值惟一的序列号码,作为某个贩卖系统的账单号码。当然,一个单例类可以持有一个聚集,从而允许存储多个状态。
没有状态的单例类
另一方面,单例类也可以是没有状态的(stateless), 仅用做提供工具性函数的对象。既然是为了提供工具性函数,也就没有必要创建多个实例,因此使用单例模式很合适。一个没有状态的单例类也就是不变(Immutable) 单例类; 关于不变模式,读者可以参见本书的"不变(Immutable )模式"一章。
多个JVM 系统的分散式系统
EJB 容器有能力将一个EJB 的实例跨过几个JVM 调用。由于单例对象不是EJB,因此,单例类局限于某一个JVM 中。换言之,如果EJB 在跨过JVM 后仍然需要引用同一个单例类的话,这个单例类就会在数个JVM 中被实例化,造成多个单例对象的实例出现。一个J2EE应用系统可能分布在数个JVM 中,这时候不一定需要EJB 就能造成多个单例类的实例出现在不同JVM 中的情况。
如果这个单例类是没有状态的,那么就没有问题。因为没有状态的对象是没有区别的。但是如果这个单例类是有状态的, 那么问题就来了。举例来说,如果一个单例对象可以持有一个int 类型的属性,用来给一个系统提供一个数值惟一的序列号码,作为某个贩卖系统的账单号码的话,用户会看到同一个号码出现好几次。
在任何使用了EJB、RMI 和JINI 技术的分散式系统中,应当避免使用有状态的单例模式。
多个类加载器
同一个JVM 中会有多个类加载器,当两个类加载器同时加载同一个类时,会出现两个实例。在很多J2EE 服务器允许同一个服务器内有几个Servlet 引擎时,每一个引擎都有独立的类加载器,经有不同的类加载器加载的对象之间是绝缘的。
比如一个J2EE 系统所在的J2EE 服务器中有两个Servlet 引擎:一个作为内网给公司的网站管理人员使用;另一个给公司的外部客户使用。两者共享同一个数据库,两个系统都需要调用同一个单例类。如果这个单例类是有状态的单例类的话,那么内网和外网用户看到的单例对象的状态就会不同。除非系统有协调机制,不然在这种情况下应当尽量避免使用有状态的单例类。
分享到:
相关推荐
入名所示,该文件为最详细的Java单例模式讲解并附有讲解代码。主要讲了单例模式的几种方法,懒汉模式、饿汉模式、静态内部类模式。着重讲解了懒汉模式下怎么实现线程安全。饿汉模式和静态内部类模式如何设置能够避免...
Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式...
目录 单例模式的概念 单例模式的要点 单例模式类图 单例模式归类 单例模式的应用场景 单例模式解决的问题 单例模式的实现方式 单例模式实现方式对比 单例模式的概念 单例模式,顾名思义就是只有一个实例,并且由它...
"设计模式单例模式和工厂模式综合应用"的主题聚焦于两种常用的设计模式:单例模式和工厂模式,并探讨它们如何协同工作来实现高效、灵活的代码结构。这个主题尤其适用于Java编程语言,因为Java的面向对象特性使得设计...
单例模式是设计模式中的一种,它的核心思想是限制类的实例化,确保在程序运行过程中,对于某个类,全局只存在一个实例。这种模式常用于资源管理器,如打印服务、数据库连接池等,因为这些资源通常需要全局共享且有序...
在Java中实现单例模式有多种方法: 1. **饿汉式(静态常量)**:在类加载时就完成了初始化,所以没有线程安全问题,但这种实现方式无法实现延迟加载。 ```java public class Singleton { private static final ...
Java设计模式之单例模式的七种写法 单例模式是一种常见的设计模式,它确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机的驱动程序对象常...
JAVA设计模式之单例模式(完整版)1[定义].pdf
作为对象的创建模式[GOF95], 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
本篇将深入探讨标题中提及的几种设计模式:Model-View-Controller(MVC)模式、单例模式、代理模式以及工厂模式,尤其是简单工厂模式。 **1. Model-View-Controller (MVC) 模式** MVC模式是一种架构模式,它将应用...
单例模式是软件设计模式中的一种经典模式,用于确保一个类只有一个实例,并提供一个全局访问点。在Java中,有多种实现单例模式的方法,每种都有其特点和适用场景。接下来,我们将深入探讨这些实现方式。 首先,我们...
单例模式是设计模式中的一种,它用于控制类的实例化过程,确保一个类在整个程序运行期间只有一个实例存在。这种模式常用于资源管理,比如管理打印机、通信端口或者系统配置等,因为这些资源通常需要全局共享且初始化...
单例模式是软件设计模式中的一种经典模式,它保证了类只有一个实例存在,并提供一个全局访问点。在Java等面向对象编程语言中,单例模式常用于管理共享资源,如数据库连接池、线程池或者配置文件等。结合工厂模式,...
Java设计模式之单例模式详解 一、单例模式概览 单例模式(Singleton Pattern)是面向对象设计模式中的一种,属于创建型模式。它确保一个类仅有一个实例,并提供一个全局访问点来访问该实例。单例模式的核心在于控制...
MVC模式使得用户界面与数据逻辑分离,单例模式保证关键资源的唯一访问点,代理模式在访问对象前后增加额外的功能,工厂模式将对象创建与使用分离,简单工厂模式为对象创建提供一个简单便捷的接口。通过这些模式,...
单例模式是软件设计模式中的一种经典模式,它在Java编程中被广泛使用,尤其是在需要控制实例化过程,或者确保某类只有一个实例时。本文将深入探讨Java中的单例模式,帮助你理解其原理和应用。 单例模式的核心思想是...
在Java编程中,单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。这种模式在需要频繁创建和销毁对象的场景中尤其有用,因为它可以节省系统资源并确保对象间的协调一致。以下是...
Java设计模式中的单例模式是一种常用的创建型设计模式,它保证了类只有一个实例,并提供一个全局访问点。这种模式在很多场景下非常有用,比如控制共享资源、管理配置信息等。接下来,我们将深入探讨8种不同的单例...
Java设计模式-单例模式详解 单例模式是 Java 设计模式中的一种常用的设计模式,旨在保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式的目的是为了保证在一个进程中,某个类有且仅有一个实例。 ...
单例模式是软件设计模式中的经典模式之一,其主要目的是控制类的实例化过程,确保在应用程序的整个生命周期中,某个类只有一个实例存在。这样的设计通常适用于那些需要频繁创建和销毁,但资源消耗较大的对象,如...