Singleton 指仅仅被实例化一的类,通常用于代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统。
使类成为Singleton 会使它的客户端测试变得困难,因为无法给Singleton 替换模拟实现,除非它实现一个充当其类型的接口。
实现Singleton 共有3种方法,在Java 1.5发行版之前实现有两种;在Java 1.5发行版之后实现Singleton 还有第三种方法,下面进行依次介绍。
构造器保持为私有,导出公有静态成员,客户端可以访问该类的唯一实例。
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
//构造器保持为私有,导出公有静态成员,客户端可以访问该类的唯一实例
private Elvis(){
//...
}
public void leaveTheBuilding(){
//...
}
}
私有构造器仅被调用依次,用来实例化公有静态final域 Elvis.INSTANCE。由于缺少共有的或者受保护的构造器,所以保证了Elvis 全局唯一性。
但要注意一点:享有的客户端可以借助 AccessiableObject.setAccessible方法,通过反射机制调用私有构造器。如果需要抵御这种攻击,可以修改构造器,代码里面做个检测判断,在被要求创建第二个实例时抛出异常。
实现Singleton 的第二种方法,公有的成员是个静态工厂方法:
public class Elvis2 {
// 私有成员
private static final Elvis2 INSTANCE = new Elvis2();
// 静态方法公有成员
public static Elvis2 getInstance(){
return INSTANCE;
}
//构造器保持为私有,导出公有静态成员,客户端可以访问该类的唯一实例
private Elvis2(){
//...
}
public void leaveTheBuilding(){
//...
}
}
对于静态方法Elvis2.getInstance() 的所有调用,都会返回一个对象引用,所以永远不会创建其他实例(但是反射机制依然可以绕开这个设计,因此最好构造器同样做个判断)。
公有域方法的好处是,组成类成员的声明,很清楚的表明这个类是一个Singleton。公有的静态域是final的, 所以该域总是包含相同的对象引用。
但公有域方法在性能上不再有任何优势了,因为现代的JVM 实现几乎都能够将静态工厂方法的调用内联化(Inline Method)。
另外,工厂方法有2个优势:
// readResolve method to preserve singleton property
private Object readObject(){
// Return the one true Elvis and let the garbage collector take care of the Elvis impersonator
return INSTANCE;
}
第三种实现Singleton 的方法是编写一个包含单个元素的枚举类型:
public class SingletonCase6 {
enum SingletonEnum {
//创建一个枚举对象,该对象天生为单例
INSTANCE;
private SingletonCase6 singleton;
//私有化枚举的构造函数
private SingletonEnum() {
System.out.println("-----1 init constructor-----");
singleton = new SingletonCase6();
}
public SingletonCase6 getSingleton() {
System.out.println("-----2 return singleton-----");
return singleton;
}
}
private SingletonCase6() {
}
public static SingletonCase6 getInstance(){
System.out.println("-----3 getInstance -----");
return SingletonEnum.INSTANCE.getSingleton();
}
public static void main(String[] args) {
getInstance();
}
}
输出结果:
-----3 getInstance -----
-----1 init constructor-----
-----2 return singleton-----
这种方法在功能上跟公有域方法相近,但它更加简洁,无偿的提供了序列化机制,绝对防止多次实例化,即使是面对复杂的序列化或者反射攻击的时候。
上面的第三种方法元素的枚举类型已经成为实现Singleton 最佳方法。
单例模式实际上有5种(懒汉模式/饿汉模式/双锁检测模式/内部类/枚举类),相关的单例模式参考文章: