由于无法模拟替换单例,使测试更加困难,除非单例实现了自己的接口。
有两种比较常见的方式来实现单例:都是通过私有构造器和暴露静态成员来访问实例的方式
第一种:final的静态成员变量
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}
初始化静态成员变量INSTANCE 时只调用私有构造函数一次。
但是通过反射并且设置AccessibleObject.setAccessible可以创建多个实例。
如果不允许这样做可以在构造函数中做判断并抛出异常。
优点:
由于final类型的成员变量,所以它只会总是包含同一个对象的引用,并且很简单
第二种:提供静态工厂方法
// Singleton with static factory
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public static Elvis getInstance() { return INSTANCE; }
public void leaveTheBuilding() { ... }
}
此方法也有同样的缺点:通过反射并且设置AccessibleObject.setAccessible可以创建多个实例。
优点:
可灵活的返回任何对象,而不用改变API;同时这个方式可以确保单个线程范围的单例
可以编写泛型的单例工厂
静态工厂可以作为一个Supplier:
Elvis::instance 是一个 Supplier<Elvis>
防止通过反序列化产生多个对象
对于可序列化的单例对象,可以设置实例字段为transient,并且提供readResolve方法
// readResolve method to preserve singleton property
private Object readResolve() {
// Return the one true Elvis and let the garbage collector
// take care of the Elvis impersonator.
return INSTANCE;
}
第三种:单元素的枚举
// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
单元素的枚举是实现单例的最佳方式
网友评论