Java 程序经验小结:用私有构造器或者枚举类型强化 Singleton 属性
1、写在开头
Singleton 指仅仅被实例化一的类,通常用于代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统。
使类成为 Singleton 会使它的客户端测试变得困难,因为无法给 Singleton 替换模拟实现,除非它实现一个充当其类型的接口。
实现 Singleton 共有 3 种方法,在 Java 1.5 发行版之前实现有两种;在 Java 1.5 发行版之后实现 Singleton 还有第三种方法,下面进行依次介绍。
2、第一种:公有静态类是个 final 域
构造器保持为私有,导出公有静态成员,客户端可以访问该类的唯一实例。
但要注意一点:享有的客户端可以借助 AccessiableObject.setAccessible 方法,通过反射机制调用私有构造器。如果需要抵御这种攻击,可以修改构造器,代码里面做个检测判断,在被要求创建第二个实例时抛出异常。
3、第二种:公有成员是个静态工厂方法
实现 Singleton 的第二种方法,公有的成员是个静态工厂方法:
公有域方法的好处是,组成类成员的声明,很清楚的表明这个类是一个 Singleton。公有的静态域是 final 的, 所以该域总是包含相同的对象引用。
但公有域方法在性能上不再有任何优势了,因为现代的 JVM 实现几乎都能够将静态工厂方法的调用内联化(Inline Method)。
另外,工厂方法有 2 个优势:
在不改变 API 的前提下,我们可以改变此类是否应该为 Singleton 的想法。工厂方法返回该类的唯一实例,但会容易被修改,比如改为每个调用该方法的线程返回一个唯一的实例。
第二个优势,与泛型有关。为了让 Singleton 类变成可序列化的(Serializable),仅仅在对象声明中加上“implements Serializable” 是不够的。为了维护并保证 Singleton,必须声明所有实例域都是瞬时的(transient),并提供一个 readResolve 方法。否则每次反序列化都会创建一个新实例。
4、第三种:包含单个元素的枚举类型
第三种实现 Singleton 的方法是编写一个包含单个元素的枚举类型:
输出结果:
这种方法在功能上跟公有域方法相近,但它更加简洁,无偿的提供了序列化机制,绝对防止多次实例化,即使是面对复杂的序列化或者反射攻击的时候。
5、总结
上面的第三种方法元素的枚举类型已经成为实现 Singleton 最佳方法。
单例模式实际上有 5 种(懒汉模式/饿汉模式/双锁检测模式/内部类/枚举类),可以参考相关的单例模式文章:
版权声明: 本文为 InfoQ 作者【后台技术汇】的原创文章。
原文链接:【http://xie.infoq.cn/article/9298a0c4f62a5d9e2640e1982】。文章转载请联系作者。
评论