Update 03. 使用私有构造方法或枚类实现Singleton属性.md

This commit is contained in:
Joe 2019-08-04 22:03:53 +08:00 committed by GitHub
parent c5fbb35f5c
commit 0cfa1275ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -33,9 +33,9 @@ public class Elvis {
  公共属性方法的主要优点是 API 明确表示该类是一个单例:公共静态属性是 final 的,所以它总是包含相同的对象引用。 第二个好处是它更简单。
  静态工厂方法的一个优点是,它可以灵活地改变你的想法,无论该类是否为单例而不必更改其 API。 工厂方法返回唯一的实例,但是可以修改,比如,返回调用它的每个线程的单独实例。 第二个好处是如果你的应用程序需要它可以编写一个泛型单例工厂generic singleton factory 详见第30 条)。 使用静态工厂的最后一个优点是方法引用可以用 `supplier`,例如 `Elvis::instance` 等同于 `Supplier<Elvis>`。 除非与这些优点相关的,否则公共属性方法是可取的
  静态工厂方法的优势之一在于,它提供了灵活性:在不改变其 API 的前提下,我们可以改变该类是否应该为单例的想法。工厂方法返回该类的唯一实例,但是,它很容易被修改,比如,改为每个调用该方法的线程返回一个唯一的实例。 第二个好处是如果你的应用程序需要它可以编写一个泛型单例工厂generic singleton factory 详见第30 条)。 使用静态工厂的最后一个优点是可以通过方法引用method reference作为提供者例如 `Elvis::instance` 等同于 `Supplier<Elvis>`。 除非满足以上任意一种优势否则还是优先考虑公有域public-field的方法
  创建一个使用这两种方法的单例类(第 12 章),仅仅将 `implements Serializable` 添加到声明中是不够的。为了保证单例模式不被破坏,声明所有的实例属性`transient`,并提供一个 `readResolve` 方法(详见第 89 条)。否则,每当序列化实例被反序列化时,就会创建一个新的实例,在我们的例子中,导致出现新的 Elvis 实例。为了防止这种情况发生,将这个 `readResolve` 方法添加到 Elvis 类:
  为了将上述方法中实现的单例类变成是可序列化的 (第 12 章),仅仅将 `implements Serializable` 添加到声明中是不够的。为了保证单例模式不被破坏,必须声明所有的实例字段`transient`,并提供一个 `readResolve` 方法(详见第 89 条)。否则,每当序列化实例被反序列化时,就会创建一个新的实例,在我们的例子中,导致出现新的 Elvis 实例。为了防止这种情况发生,将如下的 `readResolve` 方法添加到 Elvis 类:
```java
// readResolve method to preserve singleton property
@ -56,4 +56,4 @@ public enum Elvis {
}
```
  这种方式类似于公共属性方法,但更简洁,提供了免费的序列化机制,并提供了针对多个实例化的坚固保证,即使是在复杂的序列化或反射攻击的情况下。这种方法可能感觉有点不自然,但是单一元素枚举类通常是实现单例的最佳方式。注意,如果单例必须继承 `Enum` 以外的父类 (尽管可以声明一个 `Enum` 来实现接口),那么就不能使用这种方法。
  这种方式类似于公共属性方法,但更简洁,无偿地提供了序列化机制,并提供了防止多个实例化的坚固保证,即使是在复杂的序列化或反射攻击的情况下。这种方法可能感觉有点不自然,但是 **单一元素枚举类通常是实现单例的最佳方式**。注意,如果单例必须继承 `Enum` 以外的父类 (尽管可以声明一个 `Enum` 来实现接口),那么就不能使用这种方法。