Effective Java —— 用私有构造器或枚举类型强化单例属性
本文参考
本篇文章参考自《Effective Java》第三版第三条"Enforce the singleton property with a private constructor or an enum type"
原文的不足之处
原文给的示例代码比较简单,提供的是一种饿汉式的单例模式
// Singleton with public final field
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}
我们也可以将INSTANCE实例的pulice改为private,通过另一个静态方法获取实例
// 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() { ... }
}
构造器的private关键字修饰使得无法通过new关键字创建其它实例,final关键字的修饰使得INSTANCE总是包含相同的对象引用,这两种写法都能保证全局下仅有一个Elvis类的实例化对象
因为饿汉模式下,全局的单例实例在类装载时就进行构建,可能由于初始化的太早,会造成资源的浪费,所以有另一种懒汉式的单例模式,但原文中未做介绍,因为它在多线程下存在实例重复构建的问题,若要解决这个问题,会加大文章篇幅,而且也不是第三条建议的重点,更多关于懒汉式的单例模式可以参考这篇文章:
https://www.jianshu.com/p/eb30a388c5fc
the best way to implement a singleton —— a single-element enum type
Java的枚举类型是一个特殊的class,并继承了Enum<T>类,若Singleton必须扩展一个超类,而不是扩展Enum 时,则不宜使用如下方法
public class Elvis {
private Elvis() {
}
public static Elvis getInstance() {
return Singleton.INSTANCE.getInstance();
}
/**
* 注意声明为private
*/
private enum Singleton {
INSTANCE;
private Elvis singleton;
/**
* 仅执行一次
*/
Singleton() {
singleton = new Elvis();
}
public Elvis getInstance() {
return singleton;
}
}
}
因为Enum<T>类实现了Serializable接口,所以为INSTANCE自动提供了序列化机制,并且编译器会为枚举类型的实例自动添加 public static final 修饰,所以枚举类型的无参构造方法仅会被执行一次
原文也指出这种方式能够应对复杂的序列化或者反射攻击的情形
provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks.