枚举类的一个用处就是作为常量存储方式之一,如下:
那么在底层,枚举类是如何实现的呢?
我们通过 java -verbose
工具查看该类如下:
一个final类继承java.lang.Enum,因此知道枚举类是不能被继承的
2.
我们可以看到我们自定义的枚举类中的属性在底层其实也是static final的常量,不过在这里还没有初始化
3.
其中putstatic字节码:为指定的类的静态域赋值,这里就是为刚才的常量赋值了。
看到这儿我们就知道Enum为什么线程安全了,它也是利用了 ClassLoader的线程安全机制(static属性由类加载器加载)
。
当然,这并不是枚举作为单例模式最佳实现方式的最有力的理由
所以这里还有一个问题:Enum会不会像其他普通类一样——即使我在类中实现单例,但我仍可通过反序列化暴力生成一个新的对象呢?
结果是true
结论:前后仍然是同一个对象,这是为什么呢?
在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObject、readObjectNoData、writeReplace和readResolve等方法。
那么,既然枚举类禁用了序列化方法,那么只有从ObjectInputSteam和ObjectOutputStream中找了:
就是这么调用的valueof方法
总结:
- 线程安全
- 不会被序列化暴力破坏单例特性