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

  Singleton指仅仅被实例化一次的类 [Gamma95]。Singleton一般表明无状态的对象,例如函数(第24项)或者本质上惟一的系统组件。使类称为Singleton会使它的客户端测试变得十分困难,由于除非它实现了做为其类型的接口,不然不可能将模拟实现替换为单例。java

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

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

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

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

// 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实例(上述提醒依然适用)。less

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

  工厂方法的优点之一在于,它提供了灵活性:在不改变其API的前提下,咱们能够改变类是否应该为Singleton的想法。工厂方法返回惟一实例,可是,它能够很容易被修改,好比改为每一个调用该方法的线程返回一个惟一的实例。第二个优势是,若是你的应用须要,你能够编写泛型单例工厂(第30项)。使用静态工厂的最后一个优势是方法参考能够用做供应商,例如Elvis::instance是供应商<Elvis>。除非这些优点中的一个是相关的,不然公共领域方法更可取。(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<Elvis> .Unless one of these advantages is relevant, the public field approach is preferable.)测试

  为了使利用这其中一种方法实现的Singleton类编程可序列化的(第12章),仅仅在声明中加上“implements Serializable”是不够的。为了维护并保证Singleton,必须声明全部实例域都是瞬时(transient)的,并提供一个readResolve方法(第89项)。不然,每次反序列化时,都会建立一个新的实例,在咱们的示例中,会致使“假冒的Elvis”。为了防止这种状况,要在Elvis类中加入下面这个readResolve方法:ui

// 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还有第三种方法。只须要编写一个包含单个元素的枚举类型:spa

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

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

我的微信公众号同步更新

相关文章
相关标签/搜索