Effective Java 2.0_中文版_Item 3
2016-10-15 18:56
344 查看
文章作者:Tyan
博客:noahsnail.com | CSDN | 简书
在1.5版本之前,有两种方式来实现单例。它们都是通过保持私有构造函数并输出一个公有静态成员来提供对类唯一实例的访问来实现的。在第一种方法中,公有静态成员被声明为final字段:
为了初始化公有静态final变量
所有
公有变量方法的主要优势在于更清晰的声明这个类是一个单例类:公有静态变量是final的,因此它总是包含同一个对象的引用。公有变量方法没有任何性能优势:现代Java虚拟机(JVM)的大多数实现都是将静态工厂方法当做内联函数来调用。
为了使上面方法实现的单例类可序列化(第11章),仅仅在它的声明中实现
在1.5版本中,有第三种实现单例的方法。简单声明一个只有一个元素的枚举类型:
这个方法除了它更简洁之外,它在功能上等价于公有变量方法,免费提供了序列化机制,并且强有力的保证了不会被多次实例化,即使是在面临复杂的序列化或反射攻击时。虽然这个方法仍没有被广泛采用,但单元素的枚举类型是实现单例的最好方式。
博客:noahsnail.com | CSDN | 简书
Item 3 用私有构造函数或枚举类型强化单例属性
单例简单来说就是一个类只被实例化一次[Gamma95, p. 127]。通常单例表示一个系统组件在本质上来说是唯一的,例如窗口管理或文件系统。一个类成为单例会使它的客户端测试变得很困难,因为不可能用伪实现来代替单例,除非它实现了一个接口,这个接口作为它的服务类型。在1.5版本之前,有两种方式来实现单例。它们都是通过保持私有构造函数并输出一个公有静态成员来提供对类唯一实例的访问来实现的。在第一种方法中,公有静态成员被声明为final字段:
// Singleton with public final field public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis() { ... } public void leaveTheBuilding() { ... } }
为了初始化公有静态final变量
Elvis.INSTANCE,私有构造函数只调用一次。公有或保护构造函数的缺失保证了全局唯一性:确切的说一旦
Elvis类初始化,将只有一个
Elvis实例存在——不会多也不会少。客户端不能改变这个情况,但要提醒一点:有特权的客户端可以借用
AccessibleObject.setAccessible方法方法,通过反射机制(Item 53)的调用私有构造函数。如果你需要抵御这种攻击,修改构造函数使它在创建第二个实例时抛出异常。
// 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实例被创建(提醒同上)。
公有变量方法的主要优势在于更清晰的声明这个类是一个单例类:公有静态变量是final的,因此它总是包含同一个对象的引用。公有变量方法没有任何性能优势:现代Java虚拟机(JVM)的大多数实现都是将静态工厂方法当做内联函数来调用。
为了使上面方法实现的单例类可序列化(第11章),仅仅在它的声明中实现
Serializable接口是不够的。为了保证单例性,你必须将所有的实例变量声明为
transient并提供一个
readResolve方法(Item 77)。否则,每次一个序列化的实例在反序列化时将会创建一个新的实例,在我们的例子中,会看到一个假的
Elvis。为了防止这种情况发生,要在
Elvis类中添加
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; }
在1.5版本中,有第三种实现单例的方法。简单声明一个只有一个元素的枚举类型:
// Enum singleton - the preferred approach public enum Elvis { INSTANCE; public void leaveTheBuilding() { ... } }
这个方法除了它更简洁之外,它在功能上等价于公有变量方法,免费提供了序列化机制,并且强有力的保证了不会被多次实例化,即使是在面临复杂的序列化或反射攻击时。虽然这个方法仍没有被广泛采用,但单元素的枚举类型是实现单例的最好方式。
相关文章推荐
- Effective Java 2.0_中文版_Item 9
- Effective Java 2.0_中文版_Item 8
- Effective Java 2.0_中文版_Item 10
- Effective Java_中文版_第一章_2.0版本
- Effective Java 2.0_中英文对照_Item 4
- Effective Java 2.0_中英文对照_Item 1
- Effective Java 2.0_中英文对照_Item 2
- EffectiveJava Item3:使用私有constructor 或者enum实现单例
- Effective Java Second Edition中文版已出版
- Effective Java Item1:优先考虑使用静态工厂方法
- Effective Java:Ch4_Class:Item14_在public类中应该使用访问方法而不是public域
- Effective Java Item12-考虑实现Comparable接口
- Effective Java: Item 24: Make defensive copies when needed
- Effective Java(2nd Edition) Item 61 适当抽象抛出的异常(译文)
- [Effective Java Distilled] Item 4 通过私有构造方法来加强化不可实例化的性质
- 对象类Effective Java:Ch3_Methods:Item11_谨慎重写clone()
- 《Effective_Java》 Item1:Consider static actor methods instead of constructors
- Effective java 主题:Effective Java Second Edition中文版已出版
- Effective Java-Item13和Item 14
- Effective Java Item3:使用私有构造方法或者枚举类型实现单例