【Effective Java 03】用私有构造器或者枚举类型强化 Singleton 属性

1. 构造单例的第一种方法:公有域方法

  • 优点:
  • 直接获取静态变量,简单
  • 缺点:
  • 享有特权的客户端可以借助 AccessibleObject.setAccessible方法,通过反射机制调用私有构造器。如果需要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常
public class ElvisA {
  public static final ElvisA INSTANCE = new ElvisA();
  private ElvisA() {
  }
  // ...
}

2. 构造单例的第二种方法:静态工厂方法

  • 优点:
  • 可以很方便地修改, 比如改为多例模式, 每次调用返回有一个新的实例
  • 如果程序需要, 可以编写一个泛型工厂
  • 可以通过方法引用作为一个提供者
  • 缺点:
public class ElvisB {
  private static final ElvisB INSTANCE = new ElvisB();
  private ElvisB() {}
  public static ElvisB getInstance() {
    return INSTANCE;
  }
  // ...
}

公有域方法与静态工厂方法的一个共同缺点是,单例对象难以需要序列化。

如果需要将单例对象序列化,仅实现Serializeable接口是不够的。

为了维护并保证单例,必须声明所有实例域是transient的,并提供一个 readResolve 方法,来进行反实例化。

否则,每次序列化的实例时,都会创建一个新的实例,。


【建议】尽可能保持简单。如果不需要使用到工厂的一些功能,则没必要使用第二种较为复杂的方法创建单例。


3. 构造单例的第三种方法:枚举类型法

  • 优点:
  • 功能上与公有域方法类似,但更为简洁
  • 无偿提供序列化机制,绝对防御多次实例化,即使是在面对复杂的序列化或者反射攻击时
  • 缺点:
  • 无法扩展超类
public enum ElvisC {
  INSTANCE;
  // ...
}

posted on 2022-03-14 20:13  Silgm  阅读(51)  评论(0)    收藏  举报

导航