【Effective Java 03】用私有构造器或者枚举类型强化 Singleton 属性
1. 构造单例的第一种方法:公有域方法
- 优点:
- 直接获取静态变量,简单
- 缺点:
- 享有特权的客户端可以借助
AccessibleObject.setAccessible
方法,通过反射机制调用私有构造器。如果需要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常
public class ElvisA {
public static final ElvisA INSTANCE = new ElvisA();
private ElvisA() {
}
// ...
}
2. 构造单例的第二种方法:静态工厂方法
- 优点:
- 可以很方便地修改, 比如改为多例模式, 每次调用返回有一个新的实例
- 如果程序需要, 可以编写一个泛型工厂
- 可以通过方法引用作为一个提供者
- 缺点:
public class ElvisB {
private static final ElvisB INSTANCE = new ElvisB();
private ElvisB() {}
public static ElvisB getInstance() {
return INSTANCE;
}
// ...
}
公有域方法与静态工厂方法的一个共同缺点是,单例对象难以需要序列化。
如果需要将单例对象序列化,仅实现Serializeable
接口是不够的。
为了维护并保证单例,必须声明所有实例域是transient
的,并提供一个 readResolve
方法,来进行反实例化。
否则,每次序列化的实例时,都会创建一个新的实例,。
【建议】尽可能保持简单。如果不需要使用到工厂的一些功能,则没必要使用第二种较为复杂的方法创建单例。
3. 构造单例的第三种方法:枚举类型法
- 优点:
- 功能上与公有域方法类似,但更为简洁
- 无偿提供序列化机制,绝对防御多次实例化,即使是在面对复杂的序列化或者反射攻击时
- 缺点:
- 无法扩展超类
public enum ElvisC {
INSTANCE;
// ...
}