单例的枚举实如今《Effective Java》中有提到,由于其功能完整、使用简洁、无偿地提供了序列化机制、在面对复杂的序列化或者反射攻击时仍然能够绝对防止屡次实例化等优势,单元素的枚举类型被做者认为是实现Singleton的最佳方法。java
其实现很是简单,以下:
public enum Singleton {
INSTANCE;
private Singleton() {}
}
下面咱们用一个枚举实现单个数据源例子来简单验证一下:
声明一个枚举,用于获取数据库链接。
public enum DataSourceEnum {
DATASOURCE;
private DBConnection connection = null;
private DataSourceEnum() {
connection = new DBConnection();
}
public DBConnection getConnection() {
return connection;
}
}
模拟一个数据库链接类:
public class DBConnection {}
测试经过枚举获取的实例是否相同:
public class Main {
public static void main(String[] args) {
DBConnection con1 = DataSourceEnum.DATASOURCE.getConnection();
DBConnection con2 = DataSourceEnum.DATASOURCE.getConnection();
System.out.println(con1 == con2);
}
}
输出结果为:true 结果代表两次获取返回了相同的实例。
下面深刻了解一下为何枚举会知足线程安全、序列化等标准。
在JDK5 中提供了大量的语法糖,枚举就是其中一种。
所谓 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并无影响,可是可是更方便程序员使用。只是在编译器上作了手脚,却没有提供对应的指令集来处理它。
就拿枚举来讲,其实Enum就是一个普通的类,它继承自java.lang.Enum类。
public enum DataSourceEnum {
把上面枚举编译后的字节码反编译,获得的代码以下:
public final class DataSourceEnum extends Enum<DataSourceEnum> {
public static final DataSourceEnum DATASOURCE;
public static DataSourceEnum[] values();
public static DataSourceEnum valueOf(String s);
static {};
}
由反编译后的代码可知,DATASOURCE 被声明为 static 的,根据在【单例深思】饿汉式与类加载 中所描述的类加载过程,能够知道虚拟机会保证一个类的<clinit>() 方法在多线程环境中被正确的加锁、同步。因此,枚举实现是在实例化时是线程安全。
接下来看看序列化问题:
Java规范中规定,每个枚举类型极其定义的枚举变量在JVM中都是惟一的,所以在枚举类型的序列化和反序列化上,Java作了特殊的规定。
在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是经过 java.lang.Enum 的 valueOf() 方法来根据名字查找枚举对象。
也就是说,如下面枚举为例,序列化的时候只将 DATASOURCE 这个名称输出,反序列化的时候再经过这个名称,查找对于的枚举类型,所以反序列化后的实例也会和以前被序列化的对象实例相同。
public enum DataSourceEnum {
由此可知,枚举天生保证序列化单例。