单例就是一个类只能被实例化一次。单例通常代表无状态的对象比如function(Item 24),或者系统某一组件,从本质上讲,这种组件是唯一的。将一个类设置为单例会使测试它的客户端变得艰难。因为想要用一个mock的实现类去代替单例是不可能的,除非这个单例实现了一个充当单例类型的接口(不是很懂,个人猜测可能是这个接口是单例类的实现接口。原文unless it implements an interfacethat serves as its type)。
通常有两种方式来实现单例。两个都是把构造方法设置成私有的,并且为访问这个唯一的对象提供一个公共的静态成员。在一个方法中,这个成员是final的类变量。
// Singleton with public final field
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() {
}
public void leaveTheBuilding() {
}
}
用来初始化公共的final的Elvis.INSTANCE的私有构造器只被调用了一次。public和protected构造器的缺失保证了“垄断的”领域:一旦Elvis被初始化,仅仅一个Elvis实例将会存在,不多不少。客户端做什么也改变不了,除非:在反射方法AccessibleObject.setAccessible的帮助下,有特权的客户端可以调用这个私有的构造方法。如果需要防御这种攻击的话,将构造器修改为:如果请求创建第二个实例,让构造方法抛出一个异常。
第二种用来实现单例的方法中,公共的成员是一个静态工厂方法:
// 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() {
}
}
所有调用Elvis.getInstance返回相对的对象引用,不会有其他的Elvis对象被创建(除了上面提到的警告)。
提供公共参数的单例方法的主要优点是API清楚的声明了类是一个单例:因为公共的静态成员是final的,所以它永远都会只包含相同的对象引用。第二种方法的有点是它比较简单。静态工厂方法的一个优点是,对于要不要将类设置成单例并且不用改变API,它提供了灵活性。现在工厂方法返回这个唯一的实例,但是它可以被修改为每个线程调用它都返回一个不同的实例。第二个优点是,如果有必要可以写generic singleton factory(Item 30)。最后一个优点是,方法引用可以充当提供者去使用,比如Elvis::instance is aSupplier<Elvis>。除非涉及这三个中的某个优点,否则第一种方法是更好的。让一个使用这两种方法的某一个来实现的单例可序列化,仅仅在类的声明上加上implements Serializable是不够的。为了保证单例,将所有的成员参数生命为transient并且提供一个readResolve方法(Item 89)。否则,每次一个序列化的实例被反序列化时,一个新的对象就会被创建,就我们的例子而言,就会导致一个假的Elvis。使用readResolve方法来防止这类情况的发生:
//readResolve method to preserve singleton property
privateObject readResolve() {
//Return the one true Elvis and let the garbage collector
//take care of the Elvis impersonator.
returnINSTANCE;
}
第三种实现单例的方式是声明一个单个成员的枚举:
public enum Elvis {INSTANCE;
publicvoid leaveTheBuilding() {
}
}