Effective第三版 中英 | 第2章 创建和销毁对象 | 用私有构造器或者枚举类型强化 Singleton 属性

Effective第三版

前言

大家好,这里是 Rocky 编程日记 ,喜欢后端架构及中间件源码,目前正在阅读 effective-java 书籍。同时也把自己学习该书时的笔记,代码分享出来,供大家学习交流,如若笔记中有不对的地方,那一定是当时我的理解还不够,希望你能及时提出。如果对于该笔记存在很多疑惑,可以先去翻阅《Java核心技术》也欢迎和我交流讨论,最后也感谢您的阅读,点赞,关注,收藏~

前人述备矣,我只是知识的搬运工,effective 书籍源码均在开源项目 java-diary 中的 code-effective-third 模块中

源代码仓库地址: https://gitee.com/Rocky-BCRJ/java-diary.git
在这里插入图片描述

第二章 创建和销毁对象

用私有构造器或者枚举类型强化 Singleton 属性

   A singleton is simply a class that is instantiated exactly once [Gamma95]. Singletons typically represent either a stateless object such as a function (Item 24) or a system component that is intrinsically unique. Making a class a singleton can make it difficult to test its clients because it’s impossible to substitute a mock implementation for a singleton unless it implements an interface that serves as its type.

  Singleton 指仅仅被实例化一次的类 [Gamma95]。Singleton 通常代表无状态的对象,例如函数(第 24 项)或者本质上唯一的系统组件。使类成为 Singleton 会使它的客户端测试变得十分困难,因为除非它实现了作为其类型的接口,否则不可能将模拟实现替换为单例。

  There are two common ways to implement singletons. Both are based on keeping the constructor private and exporting a public static member to provide access to the sole instance. In one approach, the member is a final field:

  实现单例的方法有两种。 两者都基于保持构造函数私有并导出公共静态成员以提供对唯一实例的访问。 在一种方法中,该成员是 final 字段:

// Singleton with public final field
public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() { ... }
    public void leaveTheBuilding() { ... }
}

  The private constructor is called only once, to initialize the public static final field Elvis.INSTANCE. The lack of a public or protected constructor guarantees a “monoelvistic” universe: exactly one Elvis instance will exist once the Elvis class is initialized—no more, no less. Nothing that a client does can change this, with one caveat: a privileged client can invoke the private constructor reflectively (Item 65) with the aid of the AccessibleObject.setAccessible method. If you need to defend against this attack, modify the constructor to make it throw an exception if it’s asked to create a second instance.

  私有构造器只调用一次,用来初始化静态变量Elvis.INSTANCE。由于缺少public或者protect属性的构造器,这就保证了Elvis的全局一致性:一旦Evlis类被实例化,只会存在一个Elvis实例,不多也不少。客户端所做的任何事情都无法改变这一点,但有一点需要注意:享有特权的客户端可以借助AccessibleObject.setAccessible方法反射性地调用私有构造函数(第 65 项)。如果你需要防御此攻击,请修改构造函数以使其在要求创建第二个实例时抛出异常。

  In the second approach to implementing singletons, the public member is a static factory method:

  在实现 Singleton 的第二种方法中,公有的成员是个静态工厂方法:

// 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() { ... }
}

  All calls to Elvis.getInstance return the same object reference, and no other Elvis instance will ever be created (with the same caveat mentioned earlier).

  对于静态方法Elvis.getInstance的所有调用,都会返回同一个对象引用,所以,永远不会创建其他的Elvis实例(上述提醒依然适用)。

  The main advantage of the public field approach is that the API makes it clear that the class is a singleton: the public static field is final, so it will always contain the same object reference. The second advantage is that it’s simpler.

  公有域方法的主要好处在于,组成类的成员的声明很清楚地声明了这个类是一个 Singleton:公有的静态域是 final 的,所以该域总是包含同一个对象的引用。第二个好处就是它更加简单。

  One advantage of the static factory approach is that it gives you the flexibility to change your mind about whether the class is a singleton without changing its API. The factory method returns the sole instance, but it could be modified to return, say, a separate instance for each thread that invokes it. A second advantage is that you can write a generic singleton factory if your application requires it (Item 30). A final advantage of using a static factory is that a method reference can be used as a supplier, for example Elvis::instance is a Supplier. Unless one of these advantages is relevant, the public field approach is preferable.

  工厂方法的优势之一在于,它提供了灵活性:在不改变其 API 的前提下,我们可以改变类是否应该为 Singleton 的想法。工厂方法返回唯一实例,但是,它可以很容易被修改,比如改成每个调用该方法的线程返回一个唯一的实例。第二个优点是,如果你的应用需要,你可以编写泛型单例工厂(第 30 项)。使用静态工厂的最后一个优点是一个方法引用可以当成一个提供者,例如Elvis::instance提供的是<Elvis>。除非跟这些优点有所关联,否则使用公有域的方法更可取。

  To make a singleton class that uses either of these approaches serializable(Chapter 12), it is not sufficient merely to add implements Serializable to its declaration. To maintain the singleton guarantee, declare all instance fields transient and provide a readResolve method (Item 89). Otherwise, each time a serialized instance is deserialized, a new instance will be created, leading, in the case of our example, to spurious Elvis sightings. To prevent this from happening, add this readResolve method to the Elvis class:

  为了利用这其中一种方法实现的 Singleton 类变成可序列化的(第 12 章),仅仅在声明中加上“implements Serializable”是不够的。为了维护并保证 Singleton,必须声明所有实例域都是瞬时(transient)的,并提供一个readResolve方法(第 89 项)。否则,每次反序列化时,都会创建一个新的实例,在我们的示例中,会导致“假冒的 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;
}

  实现 Singleton 还有第三种方法。只需要编写一个包含单个元素的枚举类型:

// Enum singleton - the preferred approach
public enum Elvis {
    INSTANCE;
    public void leaveTheBuilding() { ... }
}

  This approach is similar to the public field approach, but it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks. This approach may feel a bit unnatural, but a single-element enum type is often the best way to implement a singleton. Note that you can’t use this approach if your singleton must extend a superclass other than Enum(though you can declare an enum to implement interfaces).

  这种方法类似于使用公有域的方法,但它更简洁,免费提供序列化机制,并提供了对多个实例化的铁定保证,即使面对复杂的序列化或反射攻击。这种方法可能会有点不自然,但单元素枚举类型通常是实现单例的最佳方法。请注意,如果你的单例必须扩展Enum以外的超类,则不能使用此方法(尽管你可以声明枚举来实现接口)。

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值