1、单例模式的要点:
一是某个类只能有一个实例
二是它必须自行创建这个实例
三是它必须自动向整个系统提供这个实例
2、注意:双重检查成例在java语言中并不能成立
3、饿汉式单例类:是在java语言中实现起来最为简单的单例类,其代码如下:
public class EagerSingleton {
private static final EagerSingleton m_instance = new EagerSingleton();
/**
* 私有的默认构造器
*/
private EagerSingleton(){}
/**
* 静态工厂方法
*/
public static EagerSingleton getInstance(){
return m_instance;
}
}
从上述代码可以看出,在这个类被加载时,静态变量m_instance会被初始化,此时类的私有构造函数会被调用,这个时候,单例类的唯一实例就会被创建出来了。
java语言中单例类的一个最重要的特点是类的构造函数是私有的,从而避免外界利用构造函数直接创建出任意多的实例。值得指出的是,由于构造函数是私有的,因此此类不能被继承。
4、懒汉式单例类
与饿汉式单例类相同之处是,类的构造函数是私有的。与饿汉式单例类不同的是,懒汉式单例类在第一次被引用时将自己实例化。如果加载器是静态的,那么在懒汉式单例类被加载时不会将自己实例化。其代码如下:
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;
}
}
在上面的代码中,懒汉式单例类实现对静态工厂方法使用了同步化,已处理多线程环境。
5、饿汉式单例类在自己被加载时就将自己实例化。既是加载器是静态的,在饿汉式单例类被加载时仍会将自己实例化。但从资源利用效率角度讲,这个比懒汉式单例类稍差些。从速度和反应时间角度讲,则比懒汉式单例类稍好些。然而,懒汉式单例类在实例化时,必须处理好多个线程同时首次引用此类时的访问限制问题,特别是当单例类作为资源控制器在实例化时必然涉及资源的初始化,而资源初始化很有可能耗费时间。这意味着出现多线程时同时首次引用此类的几率变得较大。
饿汉式单例类可以再java语言内实现,但是不易在C++内实现,因为静态初始化在C++里面没有固定的顺序,因而静态的m_instance变量的初始化与类的加载顺序没有保证,可能会出问题。这就是为什么Gof在提出单例类的概念时,举得例子是懒汉式的。他们的书影响之大,以至于java语言中单例类的例子也大多是懒汉式的。实际上,本书认为饿汉式单例类更符合java语言的特点。
6、登记式单例类:是Gof为了克服饿汉式单例类以及懒汉式单例类均不可以继承的缺点而设计的。现在将他们翻译为java语言,并将它自己实例化的方式从懒汉式改为饿汉式,知识它的子类实例化的方式之鞥是懒汉式的,这是无法改变的。
登记式单例类的代码如下:
public class RegSingleton {
static private HashMap m_resgisty = new HashMap();
static {
RegSingleton x = new RegSingleton();
m_resgisty.put(x.getClass().getName(), x);
}
/**
* 保护的默认构造函数
*/
protected RegSingleton(){}
/**
* 静态工厂方法,返还此类的唯一实例
*/
static public RegSingleton getInstance(String name){
if(name == null){
name = "com.pattern.singleton.RegSingleton";
}
if(m_resgisty.get(name) == null){
try{
m_resgisty.put(name, Class.forName(name).newInstance());
}catch (Exception e) {
System.out.println("Error happened");
}
}
return (RegSingleton)(m_resgisty.get(name));
}
/**
* 一个示意性的商业方法
*/
public String about(){
return "Hello, I am RegSingleton";
}
}
它的子类RegSingletonChild需要父类的帮助才能实例化。下面是登记式单例类的子类的代码:
public class RegSingletonChild extends RegSingleton{
public RegSingletonChild(){}
/**
* 静态工厂方法
*/
static public RegSingletonChild getInstance(){
return (RegSingletonChild)RegSingleton.getInstance("com.pattern.singletion.RegSingletonChild");
}
/**
* 一个示意性的商业方法
*/
public String about(){
return "Hello, I am RegSingletonChild";
}
}
由于子类必须允许父类以构造函数调用产生实例,因此它的构造函数必须是公开的。这样一来,就等于允许了以这样的方式产生实例而不再父类的登记中。这是登记式单例类的一个确定。
由于父类的实例必须存在才可能有子类的实例,这在有些情况下是一个浪费。这是登记式单例类的另一个缺点。