1、登记式单例类(RegSingleton)
登记式单例类是为了克服普通单例模式不可继承的特点而设计的。
登记式单例类的源代码如下:(一下代码均经过测试)
//RegSingleton.java :登记式单例父类
package com.javapatterns.singleton.demos;
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)
{
e.printStackTrace();
}
}
return (RegSingleton)(m_registry.get(name));
}
//一个示意性的商业方法
public String about()
{
return "Hello,I am RegSingleton.";
}
}
//RegSingletonChild.java :登记式单例的子类
package com.javapatterns.singleton.demos;
import java.util.*;
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";
}
}
子类RegSingleytonChild需要父类的帮助才能产生对象,由于子类必须允许父类以构造函数调用产生对象,因此,它的构造函数必须是公开的。这样一来就等于允许以这样方式产生实例而不是在父类的登记中,这是登记式单例的一个缺点。
GOF曾指出,由于父类的实例必须存在才可能有子类的实例,这在有些情况下是一个浪费。这是登记式单例类的另一缺点。
在什么情况下使用单例模式:
使用条件:
在一个系统要求一个类只有一个实例时才应当使用单例模式。
切记:试图使用单例模式管理共享资源的声明周期,这是不恰当的。
单例模式的加载器问题:
class singleton
{
private singleton() {}//构造函数必须是私有的,防止外部调用,生成对象
private static final singleton aobject = new singleton();//该类的唯一对象
/*
返回改类的唯一一个对象,你仔细想想,是不是只有通过这个方法可以获
得这个类的对象
*/
public static singleton getInstance()
{
return aobject;
}
public void write()
{
System.out.println("*****************");
}
public static void main(String[] args)
{
try
{
singleton obj1 = (singleton)Class.forName("singleton").newInstance();
singleton obj2 = (singleton)Class.forName("singleton").newInstance();
System.out.println(obj1);
System.out.println(obj2);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
测试结果如下:
singleton@7259da
singleton@16930e2
这说明同一个JVM中会有多个类加载器时,当两个加载器同时加载同一个类时,会出现两个实例。在很多J2EE服务器允许同一个服务器内有多个Servlet引擎时,每一个引擎都有独立的类加载器,经由不同的类加载器加载的对象之间是绝缘的。
除非系统有协调机制,不然在这中情况下应当尽量比避免使用有状态的单例类。
JDK中的单例模式:(有兴趣的话可以查阅jdk的源代码,在jdk的安装目录下src.zip包中)
Java语言中有很多单例模式的应用实例:
Java的Runtime对象
在java内部,java.lang.Runtime对象就是一个使用单例模式的例子,每一个java应用里面,都有一个Runtime对象,通过这个对象,应用程序可以与其运行环境发生相互作用。Runtime类提供一个静态工厂方法getRuntime();
Introspector类(在调试JavaBean的BDK中使用的)
java.awt.Toolkit类,getDefaultToolkit()方法就是一个静态的方法。
2、不完全的单例类
package com.javapatterns.singleton.demos;
public class LazySingleton
{
private static LazySingleton m_instance = null;
//公开的构造方法,外界可以直接实例化
public LazySingleton()
{
}
/**
*静态工厂方法
*/
synchronized public static LazySingleton getInstance()
{
if(m_instance==null)
{
m_instance=new LazySingleton();
}
return m_instance;
}
}
以上代码和以前的代码不同之处在于,其构造函数是public的,由于外界可以通过该构造函数创建出任意个此类的对象,这违背了单例类只能有一个实例的特性,因此这个类不是完全的单例类。这种情况有时会出现,如:javax.swing.TimerQueue便是一例。
造成这种情况出现的原因:
(1)初学者的错误。许多初学者没有认识到单例的构造函数不能是公开的,因此犯下这个错误。(看来我的错误还严重,以前我竟不知道构造函数还可以是私有的,唉!悲哀啊!)
(2)当初由于考虑不周,将一个类设计成为单例类,后来发现此类应当有多于一个的实例。为弥补错误,干脆将构造函数改为公开的,以便在需要时,随时调用构造函数创建新的实例。(看来我该检讨了)
(3)设计师的java知识很好,而且也知道单例模式的正确使用方法,但是,还是有意使用这种不完全的单例模式,因为他在意使用一种“改良”的单例模式。此时,出去共有的构在函数之外,这个类必须是很好的单例模式才可以。(我的境界太低了,永远到不了,大家如果自信的话,就试试吧!男人要稳!)
至此,单例模式就结束了,我的手要抽筋了,说实话大部分是抄书《java与模式》,感谢作者阎宏博士。虽然是抄书,但是全是我读过,亲手打出来,并且部分代码是我理解后改写的。(为了初学者的易读性考虑),部分改写也许有偏差请不吝赐教,多谢多谢!