Effective Java 第三版——3. 使用私有构造方法或枚类实现Singleton属性

Tips
《Effective Java, Third Edition》一书英文版已经出版,这本书的第二版想必不少人都读过,号称Java四大名著之一,不过第二版2009年出版,到如今已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深入的变化。
在这里第一时间翻译成中文版。供你们学习分享之用。app

Effective Java, Third Edition

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

单例是一个仅实例化一次的类[Gamma95]。单例对象一般表示无状态对象,如函数(条目 24)或一个本质上惟一的系统组件。让一个类成为单例会使测试它的客户变得困难,由于除非实现一个做为它类型的接口,不然不可能用一个模拟实现替代单例。函数

有两种常见的方法来实现单例。二者都基于保持构造方法私有和导出公共静态成员以提供对惟一实例的访问。在第一种方法中,成员是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方法,以反射方式调用私有构造方法(条目 65)。若是须要防护此攻击,请修改构造方法,使其在请求建立第二个实例时抛出异常。测试

在第二个实现单例的方法中,公共成员是一个静态的工厂方法:ui

// 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实例(与前面提到的警告相同)。线程

公共属性方法的主要优势是API明确表示该类是一个单例:公共静态属性是final的,因此它老是包含相同的对象引用。 第二个好处是它更简单。翻译

静态工厂方法的一个优势是,它能够灵活地改变你的想法,不管该类是否为单例而没必要更改其API。 工厂方法返回惟一的实例,可是能够修改,好比,返回调用它的每一个线程的单独实例。 第二个好处是,若是你的应用程序须要它,能够编写一个泛型单例工厂(generic singleton factory )(条目30)。 使用静态工厂的最后一个优势是方法引用能够用supplier,例如Elvis :: instance等同于Supplier<Elvis>。 除非与这些优势相关的,不然公共属性方法是可取的。code

建立一个使用这两种方法的单例类(第12章),仅仅将implements Serializable添加到声明中是不够的。为了维护单例的保证,声明全部的实例属性为transient,并提供一个readResolve方法(条目89)。不然,每当序列化实例被反序列化时,就会建立一个新的实例,在咱们的例子中,致使出现新的Elvis实例。为了防止这种状况发生,将这个readResolve方法添加到Elvis类:对象

// 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;
}

实现一个单例的第三种方法是声明单一元素的枚举类:blog

// Enum singleton - the preferred approach
public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding() { ... }
}

这种方式相似于公共属性方法,但更简洁,提供了免费的序列化机制,并提供了针对多个实例化的坚固保证,即便是在复杂的序列化或反射攻击的状况下。这种方法可能感受有点不天然,可是单一元素枚举类一般是实现单例的最佳方式。注意,若是单例必须继承Enum之外的父类(尽管能够声明一个Enum来实现接口),那么就不能使用这种方法。

相关文章
相关标签/搜索