为什么要用枚举实现Singleton--Java
原文地址:http://www.cnblogs.com/AprilCal/p/5426007.html
- 理由一:无需再考虑可序列化的情况
《effective java》第77条:对于实例控制,枚举类型优先于readResolve
说到readResolve,有的人可能会不甚清楚其作用,简单来说,readResolve的作用是这样的:readResolve特性允许你用readObject创建的实例代替另一个实例。
对于一个正在被反序列化的对象,如果他的类定义了一个readResolve方法,并具备正确的声明,那么在反序列化之后,新建对象上的readResolve方法就会被调用。然后,该方法返回的对象引用将会被返回,取代新建的对象。《effective java》中 第三条提到,如果在这个类的声明中加上了‘inplments
Serializable’的字样,它就不再是一个Singleton。无论该类使用了默认的序列化形式,还是自定义的序列化形式,都没有关系;也跟它是否提供了显示的readObject方法无关。任何一个readObject方法,不管是显示的还是默认的,它都会返回一个新建的实例。这个新建的实例不同于该类初始化时创建的实例。这也是为什么要提供readResolve方法的原因。《effective java》第3条中:
To make a singleton class that is implemented using either of the
previous approaches serializable (Chapter 11), it is not sufficient
merely to add implements Serializable to its declaration. To maintain
the singleton guarantee, you have to declare all instance fields
transient and provide a readResolve method (Item 77). Otherwise, each
time a serialized instance is deserialized, a new instance will be
created, leading, in the case of our example, to spurious Elvis
sightings. To prevent this, add this readResolve method to the Elvis
class:
// 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类可序列化,仅仅在声明中加上"implements
Serializable"是不够的。为了保持Singleton,你需要声明所有实例字段为transient并提供readResolve方法。否则,每次一个序列化的实例被反序列化时,都会创建一个新的实例。比如在我们的例子中,会导致假冒的Elvis情况。为防止此种情况,要在Elvis类中加入下面这个readResolve方法:
看到这里应该明白,对于一个需要序列化的Singleton来说,我们需要手动为其添加readResolve方法,在某些情况下,这样做会尤为复杂。
而用枚举来实现Singleton则完全不必考虑,因为jvm可以保证这一点。 理由二:无需再考虑通过反射调用私有构造函数的情况
《effective java》第三条中:享有特权的客户端可以借助AccessiableObject.setAccessible方法,通过反射机制调用私有构造器。如果需要抵御这种攻击,可以修改构造器,让他在被要求第二次创建第二个实例的时候抛出异常。
先看一个例子
package singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
//AprilCal on 2016.4.23
class Singleton//最普通的一种Singleton实现
{
private static Singleton INSTANCE=new Singleton();
private Singleton()
{
System.out.println("私有构造函数被调用");
}
public static Singleton getInstance()
{
return INSTANCE;
}
}
enum EnumSingleton2//枚举实现的Singleton
{
INSTANCE;
private EnumSingleton2()
{
System.out.println("enum 私有构造函数被调用");
}
}
public class SingletonTest
{
public static void main(String[] args)
throws ClassNotFoundException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException,
NoSuchMethodException, SecurityException, InstantiationException
{
Class <?> cls1 = Class.forName("singleton.Singleton");
Class <?> cls2 = Class.forName("singleton.EnumSingleton2");
//通过反射调用Singleton的构造函数
Constructor <?> c0=cls1.getDeclaredConstructor();
c0.setAccessible(true);
Singleton s=(Singleton)c0.newInstance();
//通过反射调用EnumSingleton的构造函数
Constructor <?> c1=cls2.getDeclaredConstructor();
c1.setAccessible(true);
EnumSingleton es=(EnumSingleton)c1.newInstance();
}
}
这段代码的执行结果如下:
私有构造函数被调用
enum 私有构造函数被调用
私有构造函数被调用
Exception in thread "main" java.lang.NoSuchMethodException: singleton.EnumSingleton2.()
at java.lang.Class.getConstructor0(Unknown Source)
at java.lang.Class.getDeclaredConstructor(Unknown Source)
at singleton.SingletonTest.main(SingletonTest.java:46)
>
在普通的Singleton中,我们通过反射机制调用了其私有的构造函数,而在通过反射调用enmuSingleton的私有构造函数时,则直接抛出了异常。可见,在使用枚举实现Singleton时,jvm会替我们完成防止通过反射调用私有构造函数的工作。
理由三:枚举实例创建是线程安全的,无需再考虑Double checked locking
我们都知道,在延迟初始化的情况下,为了保证线程安全,通常在实现Singleton的时候使用Double checked locking,而在枚举的情况下,我们则可以完全不必考虑这些。
关于Double checked locking实现Singleton详见http://www.cnblogs.com/techyc/p/3529983.html
并且此连接中有处细节需要纠正一下,用到Double checked locking时,必须要用volatile修饰单例的实例,否则将毫无意义。
至于为什么要用volatile修饰,我不说了,嘻嘻。总结
总结起来使用枚举类型实现Singleton主要有以下三大优势:
1:无需再考虑可序列化的情况
2:无需再考虑通过反射调用私有构造函数的情况
3:枚举实例创建是线程安全的
最后,千言万语一个字,使用枚举实现单例情况会好的多,但不排除某些情况用特殊的方法实现单例也同样很高效。
为什么要用枚举实现Singleton--Java的更多相关文章
- Singleton.java.ft not found 相关错误的解决办法
Entry fileTemplates//Singleton.java.ft not found in C:/Users/admin/Desktop/android-studio/lib/resour ...
- 单例模式的七种实现-Singleton(Java实现)
1. 饿汉式 实现代码: public class Singleton { private Singleton() { } private static Singleton singleton = n ...
- 用枚举来处理java自定义异常
在系统开发过程中,总少不免要自己处理一些异常信息,然后将异常信息变成友好的提示返回到客户端的这样一个过程,之前都是new一个自定义的异常,当然这个所谓的自定义异常也是继承RuntimeExceptio ...
- Singleton(Java)
1.定义私有静态易变的类变量2.定义getInstance静态方法 2.1.若静态变量为null,则在同步类类型的同时判断静态实例是否为null, 是null则创建新实例赋给静态变量 2.2.不为nu ...
- 单元素枚举类型singleton模块
public enum Elvis { INSTANCE; public void leaveTheBuilding() { System.out.println("Whoa baby, I ...
- Java中的GOF23(23中设计模式)--------- 单例模式(Singleton)
Java中的GOF23(23中设计模式)--------- 单例模式(Singleton) 在Java这这门语言里面,它的优点在于它本身的可移植性上面,而要做到可移植的话,本身就需要一个中介作为翻译工 ...
- Java枚举enum以及应用:枚举实现单例模式
枚举作为一个常规的语言概念,一直到Java5才诞生不得不说有点奇怪,以至于到现在为止很多程序员仍然更喜欢用static final的形式去命名常量而不使用,一般情况下,Java程序员用这种方式去实现枚 ...
- Java 单例(Singleton)模式
一.什么是单例模式: 单例模式是一种确保了一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.被实例化的类称为单例类. 二.单例模式的特点: 单例类只有一个实例. 单例类必须自行创建自己唯一的 ...
- 理解Java枚举类型
(参考资料:深入理解java enum) 1.原理:对编译后的class文件javap反编译可以看出,定义的枚举类继承自java.lang.Enum抽象类且通过public static final定 ...
- 用私有构造器或枚举类型强化Singleton
Singleton指只有一个实例的类,只能被创建一次. 在Java1.5之前实现Singleton有两种方式,都是将构造器设为private并导出公有的静态成员实例. 第一种方式将公有的静态成员实例设 ...
随机推荐
- 常见的生成全局唯一id有哪些?他们各有什么优缺点?
分布式系统中全局唯一id是我们经常用到的,生成全局id方法由很多,我们选择的时候也比较纠结.每种方式都有各自的使用场景,如果我们熟悉各种方式及优缺点,使用的时候才会更方便.下面我们就一起来看一下常见的 ...
- OO 第四单元总结
一.总结本单元两次作业的框架设计 1.1. 需求分析 通过分析mdj文件可知,两次作业如果对于时间复杂度没有要求,可以不涉及任何数据结构,直接根据读入的UML_ELEMENT逐个分析得到各个函数的结果 ...
- 学习笔记:MDN的HTML
一. HTML介绍: HTML —— 用于定义一个网页的结构的基本技术. 元素(Element):开始标记,加结束标记,加内容,等于元素. 两种重要的元素类别,块级元素和内联元素: 块级 ...
- poj3334(Connected Gheeves)
Connected Gheeves Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 1008 Accepted: 368 ...
- C# 只运行一个实例 ShowWindowAsync 窗体隐藏时失效 解决方案
如果窗体已经隐藏,那么利用instance.MainWindowHandle得到的句柄为空,继而ShowWindowAsync 操作失败 不过我们可以使用FindWindow来查找到指定窗体的句柄 只 ...
- Eucalyptus(v4.0)系统需求
1.计算需求 Physical Machines: All Eucalyptus components must be installed on physical machines, not virt ...
- C++编写字符串类CNString,该类有默认构造函数、类的拷贝函数、类的析构函数及运算符重载
编码实现字符串类CNString,该类有默认构造函数.类的拷贝函数.类的析构函数及运算符重载,需实现以下“=”运算符.“+”运算.“[]”运算符.“<”运算符及“>”运算符及“==”运算符 ...
- Html+css实现带图标的控件
</pre><pre name="code" class="html"><!DOCTYPE html> <html l ...
- pta 编程题13 File Transfer
其它pta数据结构编程题请参见:pta 这道题考察的是union-find并查集. 开始把数组中每个元素初始化为-1,代表没有父节点.为了使树更加平衡,可以让每一个连通分量的树根的负值代表这个连通分量 ...
- 微信小程序 尺寸单位px与rpx之间的转换(入门篇)
1.rpx:微信小程序中的尺寸单位rpx(responsive pixel):可以根据屏幕宽度进行自适应.规定屏幕宽度为750rpx. 微信官方建议视觉稿以iphone6为标准. 2.个人示例测试: ...