目前在读阎宏的这本《Java与模式》,看到了单例模式,总结一下。
(注明:本文中的代码是从阎宏老师书中拷贝过来的)
在书中提到了饿汉式、懒汉式以及登记式单例类。
饿汉式的结构如下
public class EagerSingleton
{
private static final EagerSingleton m_instance = new EagerSingleton();
/**
* 私有的默认构造方法
*/
private EagerSingleton(){}
/**
* 静态工厂方法
*/
public static EagerSingleton getInstance()
{
return m_instance;
}
}
从代码结构上看,当EagerSingleton类加载时,就会实例化静态变量m_instance,与此同时私有的构造方法也被调用到。这样EagerSingleton的唯一实例就被创建了。(题外话,为啥叫饿汉式呢?当一个人饿的时候,会不考虑其他的,首先先吃点儿东西再说,那么这里就是先调用一次构造方法再说咯,呵呵……阎宏老师的书中有很多这样的类比)
懒汉式的结构如下:
public class LazySingleton
{
private static LazySingleton m_instance = null;
/**
* 私有的默认构造方法,保证外界无法直接实例化
*/
private LazySingleton() {}
/**
* 静态工厂方法,返还此类的唯一实例
*/
synchronized public static LazySingleton getInstance()
{
if ( m_instance == null )
{
m_instance = new LazySingleton();
}
return m_instance;
}
}
从代码结构上看,当LazySingleton类加载时,首先会对m_instance静态变量实例化一个null对象,当真正需要使用LazySingleton类时调用getInstance()方法返回一个LazySingleton类实例(题外话,只有需要时才会去使用,比较懒的做法,不会提前准备好,因此叫懒汉式)。注意到,在获得实例的方法前面加了关键字synchronized,这个关键字一般是为了保证线程安全时才会被使用,这里被阎宏老师说成了“双重检查成例”。如果不加可不可以呢?可以,不过要在单线程环境下运行。如果在多线程环境下运行会出现产生多实例争夺资源的情况,我们不能保证计算机会不会在同一时间里多线程调用getInstance方法。因此必须注意在实例化时多线程的同时首次引用此类时的访问权限。
登记式单例类(也叫注册式单例类)的结构如下
package com.javapatterns.singleton.demos;
import java.util.HashMap;
public class RegSingleton
{
private static HashMap m_registry = new HashMap();
static
{
RegSingleton x = new RegSingleton();
m_registry.put(x.getClass().getName(), x);
}
/**
* 保护的默认构造方法
*/
protected RegSingleton() {}
/**
* 静态工厂方法,返还此类的唯一实例
*/
public static RegSingleton getInsatnce(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 happend.");
}
}
return (RegSingleton) m_registry.get(name);
}
/**
* 一个示意性的商业方法
*/
public String about()
{
return "Hello, I am RegSingleton";
}
}
登记式单例方法解决了前面两个列子无法解决的子类继承问题。在本例中,阎宏老师将实例化的方式从懒汉式改为了饿汉式,只是它的子类实例化方式只能是懒汉式的,且无法改变。
下面是子类的代码结构
package com.javapatterns.singleton.demos;
import java.util.HahMap;
public class RegSingletonChild extends RegSingleton
{
public RegSingletonChild() {}
/**
* 静态方法工厂
*/
public static RegSingletonChild getInstance()
{
return (RegSingletonChild) RegSingleton.getInstance("com.javapatterns.singleton.demos.RegSingletonChild");
}
/**
* 一个示意性的商业方法
*/
public String about()
{
return "Hello, I am RegSingletonChild";
}
}
由于子类必须允许弗雷以构造方法调用产生实例,因此,它的构造方法必须是公开的。这样一来,就等于允许了以这样的方式产生实例而不在父类的登记中。这是登记式单例类的一个缺点。
各尽所需,各取所求,实现的方式有很多种,根据自己的系统需要选用不同的设计模式。