前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一文读懂《Effective Java》第3条:用私有构造器或者枚举类型强化Singleton属性

一文读懂《Effective Java》第3条:用私有构造器或者枚举类型强化Singleton属性

作者头像
后台技术汇
发布2022-05-28 12:34:35
3060
发布2022-05-28 12:34:35
举报
文章被收录于专栏:后台技术汇

Singleton 指仅仅被实例化一的类,通常用于代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统。

使类成为Singleton 会使它的客户端测试变得困难,因为无法给Singleton 替换模拟实现,除非它实现一个充当其类型的接口。

实现Singleton 共有3种方法,在Java 1.5发行版之前实现有两种;在Java 1.5发行版之后实现Singleton 还有第三种方法,下面进行依次介绍。

第一种:公有静态类是个final域

构造器保持为私有,导出公有静态成员,客户端可以访问该类的唯一实例。

代码语言:javascript
复制
public class Elvis {

  public static final Elvis INSTANCE = new Elvis();

  //构造器保持为私有,导出公有静态成员,客户端可以访问该类的唯一实例
  private Elvis(){
    //...
  }

  public void leaveTheBuilding(){
    //...
  }
}

私有构造器仅被调用依次,用来实例化公有静态final域 Elvis.INSTANCE。由于缺少共有的或者受保护的构造器,所以保证了Elvis 全局唯一性。

但要注意一点:享有的客户端可以借助 AccessiableObject.setAccessible方法,通过反射机制调用私有构造器。如果需要抵御这种攻击,可以修改构造器,代码里面做个检测判断,在被要求创建第二个实例时抛出异常。

第二种:公有成员是个静态工厂方法

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

代码语言:javascript
复制
public class Elvis2 {
  // 私有成员
  private static final Elvis2 INSTANCE = new Elvis2();

  // 静态方法公有成员
  public static Elvis2 getInstance(){
    return INSTANCE;
  }

  //构造器保持为私有,导出公有静态成员,客户端可以访问该类的唯一实例
  private Elvis2(){
    //...
  }

  public void leaveTheBuilding(){
    //...
  }
}

对于静态方法Elvis2.getInstance() 的所有调用,都会返回一个对象引用,所以永远不会创建其他实例(但是反射机制依然可以绕开这个设计,因此最好构造器同样做个判断)。

公有域方法的好处是,组成类成员的声明,很清楚的表明这个类是一个Singleton。公有的静态域是final的, 所以该域总是包含相同的对象引用。

但公有域方法在性能上不再有任何优势了,因为现代的JVM 实现几乎都能够将静态工厂方法的调用内联化(Inline Method)

另外,工厂方法有2个优势:

  • 在不改变API的前提下,我们可以改变此类是否应该为Singleton 的想法。工厂方法返回该类的唯一实例,但会容易被修改,比如改为每个调用该方法的线程返回一个唯一的实例。
  • 第二个优势,与泛型有关。为了让Singleton 类变成可序列化的(Serializable),仅仅在对象声明中加上“implements Serializable” 是不够的。为了维护并保证Singleton,必须声明所有实例域都是瞬时的(transient),并提供一个readResolve方法。否则每次反序列化都会创建一个新实例。
代码语言:javascript
复制
  // readResolve method to preserve singleton property
  private Object readObject(){
    // Return the one true Elvis and let the garbage collector take care of the Elvis impersonator
    return INSTANCE;
  }

第三种:包含单个元素的枚举类型

第三种实现Singleton 的方法是编写一个包含单个元素的枚举类型:

代码语言:javascript
复制
public class SingletonCase6 {
  enum SingletonEnum {
    //创建一个枚举对象,该对象天生为单例
    INSTANCE;
    private SingletonCase6 singleton;

    //私有化枚举的构造函数
    private SingletonEnum() {
      System.out.println("-----1 init constructor-----");
      singleton = new SingletonCase6();
    }

    public SingletonCase6 getSingleton() {
      System.out.println("-----2 return singleton-----");
      return singleton;
    }
  }

  private SingletonCase6() {
  }

  public static SingletonCase6 getInstance(){
    System.out.println("-----3 getInstance -----");
    return SingletonEnum.INSTANCE.getSingleton();
  }

  public static void main(String[] args) {
    getInstance();
  }
}

输出结果:

代码语言:javascript
复制
-----3 getInstance -----
-----1 init constructor-----
-----2 return singleton-----

这种方法在功能上跟公有域方法相近,但它更加简洁,无偿的提供了序列化机制,绝对防止多次实例化,即使是面对复杂的序列化或者反射攻击的时候。

总结

上面的第三种方法元素的枚举类型已经成为实现Singleton 最佳方法

单例模式实际上有5种(懒汉模式/饿汉模式/双锁检测模式/内部类/枚举类),相关的单例模式参考文章:

  • 设计模式学习(五):单例模式 (上) https://blog.csdn.net/qq_29166327/article/details/82229348?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160351095319195264762715%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=160351095319195264762715&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v1~rank_blog_v1-3-82229348.pc_v1_rank_blog_v1&utm_term=%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F&spm=1018.2118.3001.4187
  • 设计模式学习(五):单例模式 (下) https://blog.csdn.net/qq_29166327/article/details/103587276?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160351095319195264762715%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=160351095319195264762715&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v1~rank_blog_v1-4-103587276.pc_v1_rank_blog_v1&utm_term=%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F&spm=1018.2118.3001.4187
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后台技术汇 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第一种:公有静态类是个final域
  • 第二种:公有成员是个静态工厂方法
  • 第三种:包含单个元素的枚举类型
  • 总结
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档