effective-Java/ch02创建和销毁对象/03.使用私有构造方法或枚类实现Singleton属性.md

35 lines
2.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 使用私有构造方法或枚类实现Singleton属性
单例是一个仅实例化一次的类。单例对象通常表示无状态对象,如函数(条目 24)或一个本质上唯一的系统组件。让一个类成为单例会使测试它的客户变得困难,因为除非实现一个作为它类型的接口,否则不可能用一个模拟实现替代单例。
### 单例类方法一:成员是 final 修饰的属性
**示例代码**[Item03Example01.java](CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example01.java)
私有构造方法只调用一次,来初始化公共静态 final `Elvis.INSTANCE`属性。
缺少一个公共的或受保护的构造方法保证了全局的唯一性一旦Elvis类被初始化一个Elvis的实例就会存在——不多也不少。
客户端所做的任何事情都不能改变这一点,但需要注意的是:特权客户端可以使用`AccessibleObject.setAccessible`方法,以反射方式调用私有构造方法(条目 65)。
如果需要防御此攻击,请修改构造函数,使其在请求创建第二个实例时抛出异常。
### 单例类方法二:公共成员是一个静态的工厂方法
**示例代码**[Item03Example02.java](CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example02.java)
所有对`Elvis.getInstance`的调用都返回相同的对象引用并且不会创建其他的Elvis实例与前面提到的警告相同
公共属性方法的主要优点是API明确表示该类是一个单例公共静态属性是final的所以它总是包含相同的对象引用。 第二个好处是它更简单。
静态工厂方法的一个优点是它可以灵活地改变你的想法无论该类是否为单例而不必更改其API。 工厂方法返回唯一的实例,但是可以修改,比如,返回调用它的每个线程的单独实例。 第二个好处是如果你的应用程序需要它可以编写一个泛型单例工厂generic singleton factory 条目30。 使用静态工厂的最后一个优点是方法引用可以用`supplier`,例如`Elvis :: instance`等同于`Supplier<Elvis>`。 除非与这些优点相关的,否则公共属性方法是可取的。
### 单例类方法三:声明单一元素的枚举类
**示例代码**[Item03Example03.java](CreatingAndDestroyingObjects/src/main/java/com/jueee/item03/Item03Example03.java)
这种方式类似于公共属性方法,但更简洁,提供了免费的序列化机制,并提供了针对多个实例化的坚固保证,即使是在复杂的序列化或反射攻击的情况下。
这种方法可能感觉有点不自然,但是**单一元素枚举类通常是实现单例的最佳方式**。
注意,如果单例必须继承`Enum`以外的父类(尽管可以声明一个`Enum`来实现接口),那么就不能使用这种方法。