基本概念
Singleton 是一种创建性模型它用来确保只产生一个实例并提供一个访问它的全局访问点对一些类来说保证只有一个实例是很重要的比如有的时候数据库连接或 Socket 连接要受到一定的限制必须保持同一时间只能有一个连接的存在再举个例子集合中的 set 中不能包含重复的元素添加到set里的对象必须是唯一的如果重复的值添加到 set它只接受一个实例JDK中正式运用了Singleton模式来实现 set 的这一特性大家可以查看javautilCollections里的内部静态类SingletonSet的原代码其实Singleton是最简单但也是应用最广泛的模式之一在 JDK 中随处可见
简单分析
为了实现 Singleton 模式我们需要的是一个静态的变量能够在不创建对象的情况下记忆是否已经产生过实例了静态变量或静态方法都可以在不产生具体实例的情况下直接调用这样的变量或方法不会因为类的实例化而有所改变在图的结构中可以看到uniqueInstance 就是这个独立的静态变量它可以记忆对象是否已经实例化了在静态方法 Instance 中对这个变量进行判断若没有实例化过就产生一个新的对象如果已经实例化了则不再产生新的对象仍然返回以前产生的实例
图: Singleton 模式结构
具体实施
实现 Singleton 模式的办法通常有三种
一 用静态方法实现 Singleton 这种方法是使用静态方法来监视实例的创建为了防止创建一个以上的实例我们最好把构造器声明为 private
这样可以防止客户程序员通过除由我们提供的方法之外的任意方式来创建一个实例如果不把构造器声明为private编译器就会自作聪明的自动同步一个默认的friendly构造器这种实现方法是最常见的也就是图中结构的标准实现
public class Singleton {
private static Singleton s;
private Singleton(){};
/**
* Class method to access the singleton instance of the class
*/
public static Singleton getInstance() {
if (s == null)
s = new Singleton();
return s;
}
}
// 测试类
class singletonTest {
public static void main(String[] args) {
Singleton s = SingletongetInstance();
Singleton s = SingletongetInstance();
if (s==s)
Systemoutprintln(s is the same instance with s);
else
Systemoutprintln(s is not the same instance with s);
}
}
singletonTest运行结果是:
s is the same instance with s
这证明我们只创建了一个实例
二 以静态变量为标志实现 Singleton 在类中嵌入一个静态变量做为标志每次都在进入构造器的时候进行检查
问题在于构造器没有返回类型如果确定创建一个实例成功与否一个方法是调用一个函数来检查创建是否成功然后简单的返回一个来自静态变量的值但是这样做是不优雅的而且容易发生错误比较好的做法是创建一个当创建了一个以上的实例时可以抛出异常的类这个类仅仅是调用父类方法好处是用了自己命名的异常类型错误信息更加清晰:
class SingletonException extends RuntimeException {
public SingletonException(String s) {
super(s);
}
}
class Singleton {
static boolean instance_flag = false; // true if instance
public Singleton() {
if (instance_flag)
throw new SingletonException(Only one instance allowed);
else
instance_flag = true; // set flag for instance
}
}
// 测试类
public class singletonTest {
static public void main(String argv[]) {
Singleton s s;
// create one incetancethis should always work
Systemoutprintln(Creating one instance);
try {
s = new Singleton();
} catch (SingletonException e) {
Systemoutprintln(egetMessage());
}
// try to create another spooler should fail
Systemoutprintln(Creating two instance);
try {
s = new Singleton();
} catch (SingletonException e) {
Systemoutprintln(egetMessage());
}
}
}
singletonTest运行结果是:
Creating one instance
Creating two instance
Only one instance allowed
可以看出第一个实例顺利创建第二个实例创建实抛出了我们自定义的异常
三 用注册器机制来创建 Singleton 首先用集合中的Hashtable 和Enumeration来实现addItem(Object key Object value)getItem(Object key) removeItem(Object key)等方法实现一个管理器将key和value一一关联起来客户程序员创建实例前首先用addItem方法进行注册再用getItem方法获取实例Hashtable中的key是唯一的从而保证创建的实例是唯一的具体实现限于篇幅不再细说在Prototype模型的应用一文中我将会给出一个实现注册器的代码用注册器机制来创建 Singleton模式的好处是易于管理可以同时控制多个不同类型的Singleton 实例
小结
Singleton模式可以方便的进行扩充产生指定数目的实例
在The Design Patterns Java Companion 一书中曾提到过用静态类的方式来实现 Singleton模式并指出javalangMath就是一个例子这里我并不表示赞同因为Math并不是一个真正的对象我们只是直接调用Math类所包装的静态方法而已根本就没有创建实例的过程又从何说起只产生一个实例呢?这个问题我曾到Javaranch的论坛上发过帖子所有回帖的人也都是对这一观点持否定态度
在多线程的程序中singleton可能会变的不可靠可能会出现多个实例解决的办法很简单加个同步修饰符: public static synchronized Singleton getInstance() 这样就保证了线程的安全性
最后要说的是大家可能会看见一些其他实现Singleton模式的方法因为模式在具体的应用时是灵活的不是一成不变的并没有一个固定的做法但大都是上面几种方法的变形