为什么要用枚举实现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并导出公有的静态成员实例. 第一种方式将公有的静态成员实例设 ...
随机推荐
- Device
#import "AppDelegate.h" #import "RootViewController.h" @implementation AppDelega ...
- SSH隧道打洞技巧
SSH Tunnel有三种,分别是Local模式(ssh -NfL),Remote模式(ssh -NfR),Dynamic模式(ssh -NfD). 基本参数说明: -N 不执行任何命令 -f 在背景 ...
- 子元素的margin-top会影响父元素
---恢复内容开始--- 之前在写项目的时候,发现原本想让父子元素之间加点边距,却让父元素产生了margin-top,于是百度之后发现了原因. 在css2.1盒模型中 In this specific ...
- logback的加载过程
使用logback-classic.jar时,启动应用后,logback按照以下顺序进行扫描: 1.在系统配置文件System Properties中寻找是否有logback.configuratio ...
- Weblogic 10.3.6.0 集群搭建
Weblogic 集群搭建 Oracle的Weblogic分开发者版本和生产版本,有32位和64位.一般生产版本的weblogic是64位的,安装文件是一个大小为1G多的jar包.去oracle官网上 ...
- Posgtes 常见命令
postgres 版本查看命令sudo -u postgres psql --version
- Mvc重写JsonResult
用了mvc有一段时间了,慢慢的熟悉起来了,也渐渐的发现了mvc的一些缺点,比如当我们返回 Json(new{})的时候没办法做到将首字母转换成小写.日期再序列化过后是时间戳需要到前台重新处理或者提在在 ...
- HDU 2191 悼念汶川地震(多重背包)
思路: 多重背包转成01背包,怎么转?把一种大米看成一堆单个的物品,每件物品要么装入,要么不装.复杂度比01背包要大.时间复杂度为O(vns)(这里S是所有物品的数量s之和).这个做法太粗糙了,但就是 ...
- hiho一下 第三十九周 归并排序求逆序数
题目链接:http://hihocoder.com/contest/hiho39/problem/1 ,归并排序求逆序数. 其实这道题也是可以用树状数组来做的,不过数据都比较大,所以要离散化预处理一下 ...
- java Vamei快速教程02 方法和数据成员
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 在Java基础01 从HelloWorld到面向对象,我们初步了解了对象(obje ...