资讯专栏INFORMATION COLUMN

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

codeGoogle / 2911人阅读

摘要:两者都基于保持构造函数私有并导出公共静态成员以提供对唯一实例的访问。在一种方法中,该成员是字段私有构造器只调用一次,用来初始化静态变量。由于缺少或者属性的构造器,这就保证了的全局一致性一旦类被实例化,只会存在一个实例,不多也不少。

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

  实现单例的方法有两种。 两者都基于保持构造函数私有并导出公共静态成员以提供对唯一实例的访问。 在一种方法中,该成员是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的第二种方法中,公有的成员是个静态工厂方法:

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

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

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

  为了使利用这其中一种方法实现的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() { ... }
}

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/73904.html

相关文章

  • 二章 创建和销毁对象

    摘要:一个类可以提供一个公共静态工厂方法,它仅仅是一第项遇到多个构造器参数时要考虑使用构建器静态工厂和构造器有个共同的局限性他们都不能很好地扩展到大量的可选参数。   本章涉及创建和销毁对象,包括何时以及如何创建它们,何时以及如何避免创建它们,如何确保它们被及时销毁,以及如何管理在销毁之前必须进行的清理操作。 第1项:用静态工厂方法代替构造器   类允许客户端获取实例的传统方法是提供公共构造...

    Jeffrrey 评论0 收藏0
  • Effective Java 3rd.Edition 翻译

    摘要:推荐序前言致谢第一章引言第二章创建和销毁对象第项用静态工厂方法代替构造器第项遇到多个构造器参数时要考虑使用构建器第项用私有构造器或者枚举类型强化属性第项通过私有构造器强化不可实例化的能力第项优先考虑依赖注入来引用资源第项避免创建不必要的对象 推荐序 前言 致谢 第一章 引言 第二章 创建和销毁对象 第1项:用静态工厂方法代替构造器 第2项:遇到多个构造器参数时要考虑使用构建器 第...

    KoreyLee 评论0 收藏0
  • Effective Java 三版 全文翻译

    摘要:本章中的大部分内容适用于构造函数和方法。第项其他方法优先于序列化第项谨慎地实现接口第项考虑使用自定义的序列化形式第项保护性地编写方法第项对于实例控制,枚举类型优先于第项考虑用序列化代理代替序列化实例附录与第版中项目的对应关系参考文献 effective-java-third-edition 介绍 Effective Java 第三版全文翻译,纯属个人业余翻译,不合理的地方,望指正,感激...

    galois 评论0 收藏0
  • 《Effective Java》学习笔记 二章 创建和销毁对象

    摘要:第二章创建和销毁对象何时以及如何创建对象,何时以及如何避免创建对象,如何确保他们能够适时地销毁,以及如何管理对象销毁之前必须进行的各种清理动作。表示工厂方法所返回的对象类型。 第二章 创建和销毁对象 何时以及如何创建对象,何时以及如何避免创建对象,如何确保他们能够适时地销毁,以及如何管理对象销毁之前必须进行的各种清理动作。 1 考虑用静态工厂方法代替构造器 一般在某处获取一个类的实例最...

    tinylcy 评论0 收藏0

发表评论

0条评论

codeGoogle

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<