点击关注,快速进阶高级架构师

作者:Hollis

写在前面:Java SE5提供了一种新的类型-Java的枚举类型,关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能。本文将深入分析枚举的源码,看一看枚举是怎么实现的,他是如何保证线程安全的,以及为什么用枚举实现的单例是最好的方式。

枚举是如何保证线程安全的

要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和class一样,只是一个关键字,他并不是一个类,那么枚举是由什么类维护的呢,我们简单的写一个枚举:

public enum t { SPRING,SUMMER,AUTUMN,WINTER;}

然后我们使用反编译,看看这段代码到底是怎么实现的,反编译(Java的反编译)后代码内容如下:

通过反编译后代码我们可以看到,public final class T extends Enum,说明,该类是继承了Enum类的,同时final关键字告诉我们,这个类也是不能被继承的。当我们使用enmu来定义一个枚举类型的时候,编译器会自动帮我们创建一个final类型的类继承Enum类,所以枚举类型不能被继承,我们看到这个类中有几个属性和方法。

我们可以看到:

都是static类型的,因为static类型的属性会在类被加载之后被初始,当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。所以,创建一个enum类型是线程安全的

为什么用枚举实现的单例是最好的方式

在单例模式的七种写法中,我们看到一共有七种实现单例的方式,其中,Effective Java作者Josh Bloch 提倡使用枚举的方式,既然大神说这种方式好,那我们就要知道它为什么好?

1. 枚举写法简单

public enum EasySingleton{ INSTANCE;}

你可以通过EasySingleton.INSTANCE来访问。

2. 枚举自己处理序列化

我们知道,以前的所有的单例模式都有一个比较大的问题,就是一旦实现了Serializable接口之后,就不再是单例得了,因为,每次调用 readObject()方法返回的都是一个新创建出来的对象,有一种解决办法就是使用readResolve()方法来避免此事发生。但是,为了保证枚举类型像Java规范中所说的那样,每一个枚举类型极其定义的枚举变量在JVM中都是唯一的,在枚举类型的序列化和反序列化上,Java做了特殊的规定。原文如下:Enum constants are serialized differently than ordinary serializable or externalizable objects. The serialized form of an enum constant consists solely of its name; field values of the constant are not present in the form. To serialize an enum constant, ObjectOutputStream writes the value returned by the enum constant’s name method. To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the java.lang.Enum.valueOf method, passing the constant’s enum type along with the received constant name as arguments. Like other serializable or externalizable objects, enum constants can function as the targets of back references appearing subsequently in the serialization stream. The process by which enum constants are serialized cannot be customized: any class-specific writeObject, readObject, readObjectNoData, writeReplace, and readResolve methods defined by enum types are ignored during serialization and deserialization. Similarly, any serialPersistentFields or serialVersionUID field declarations are also ignored–all enum types have a fixedserialVersionUID of 0L. Documenting serializable fields and data for enum types is unnecessary, since there is no variation in the type of data sent.

大概意思就是说,在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。 我们看一下这个valueOf方法:

从代码中可以看到,代码会尝试从调用enumType这个Class对象的enumConstantDirectory()方法返回的map中获取名字为name的枚举对象,如果不存在就会抛出异常。再进一步跟到enumConstantDirectory()方法,就会发现到最后会以反射的方式调用enumType这个类型的values()静态方法,也就是上面我们看到的编译器为我们创建的那个方法,然后用返回结果填充enumType这个Class对象中的enumConstantDirectory属性。

所以,JVM对序列化有保证。

3.枚举实例创建是thread-safe(线程安全的)

我们在深度分析Java的ClassLoader机制(源码级别)和Java类的加载、链接和初始化两个文章中分别介绍过,当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。所以,创建一个enum类型是线程安全的

深度分析Java的枚举类型——枚举的线程安全性及序列化问题相关推荐

  1. 深度分析 Java 的枚举类型:枚举的线程安全性及序列化问题(转)

    写在前面: Java SE5 提供了一种新的类型 Java的枚举类型,关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能 ...

  2. 深度分析 Java 的枚举类型:枚举的线程安全性及序列化问题

    本文作者: 伯乐在线 - HollisChuang .未经作者许可,禁止转载! 欢迎加入伯乐在线 专栏作者. 写在前面: Java SE5 提供了一种新的类型 Java的枚举类型,关键字 enum 可 ...

  3. Java中枚举的线程安全性及序列化问题

    转载自  Java中枚举的线程安全性及序列化问题 Java SE5提供了一种新的类型-Java的枚举类型,关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序 ...

  4. 深度分析Java的ClassLoader机制(源码级别)

    转载自 深度分析Java的ClassLoader机制(源码级别) Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取 ...

  5. Java--枚举类型(枚举类型介绍 定义枚举类型 枚举类型的使用 枚举类型的注意事项 遍历枚举项)

    01: Java–枚举类型(枚举类型介绍 & 定义枚举类型 & 枚举类型的使用 & 枚举类型的注意事项 & 遍历枚举项) 02: Java–枚举类型(枚举类型实现接口 ...

  6. 枚举类型为什么是线程安全的?为什么反序列化枚举类型也不会创建新的实例?

    枚举类型为什么是线程安全的? 我们定义的一个枚举,在第一次被真正用到的时候,会被虚拟机加载并初始化,而这个初始化过程是线程安全的.而我们知道,解决单例的并发问题,主要解决的就是初始化过程中的线程安全问 ...

  7. 枚举是如何实现的?(枚举的线程安全性及序列化问题)

    枚举是如何实现的?(枚举的线程安全性及序列化问题) 枚举是如何保证线程安全的 举例源码 1 public enum t { 2 SPRING,SUMMER,AUTUMN,WINTER; 3 } 反编译 ...

  8. Java注解参数类型枚举值_EffectiveJava-5-枚举和注解

    用enum代替int常量 1. int枚举: 引入枚举前,一般是声明一组具名的int常量,每个常量代表一个类型成员,这种方法叫做int枚举模式. int枚举模式是类型不安全的,例如下面两组常量:性别和 ...

  9. java,证件类型枚举

    import cn.hutool.core.util.StrUtil;/*** 证件类型枚举** @author yi_sao* @date 2022/9/27*/ public enum CardT ...

最新文章

  1. 使用回调函数实现图像阈值分析。程序运行后在屏幕中输入阈值,通过改变滑动条实现不同类型的二值化图。
  2. 《ASP.NET 开发从入门到精通》----2.3 编译和部署ASP.NET程序
  3. Chrome开发者工具和Firebug的一些简单比较
  4. .Net Core3.0使用gRPC
  5. python paramiko使用_python paramiko 模块使用方法
  6. linux hid 输入设备 在window上需要额外驱动?,什么是HID兼容设备?Win10缺少HID兼容的触摸屏驱动咋办?...
  7. java计算机毕业设计高校墨香文学社管理系统源码+mysql数据库+系统+lw文档+部署
  8. 360度全景视频html,360度全景视频是怎么拍摄出来的?
  9. Contour 学习笔记(一):使用 Contour 接管 Kubernetes 的南北流量
  10. 毛毛雨 2007-11
  11. eclipse代码:1到100既是3又是5的倍数
  12. return返回值返回到哪里
  13. 全国366个市县日度空气质量数据(2016-2020年)(AQI,SO2,NO2,PM2.5,PM10)
  14. 数学中的斜积分符号还能这样打出来
  15. 国内不翻墙调用chatgpt api
  16. 如何测试服务器性能?
  17. 带你读论文系列之计算机视觉--GoogLeNet
  18. 转载好文 php读取大文件
  19. 决策树的原理及构建(基于ID3算法)
  20. JarvisOJ-握手包

热门文章

  1. js-ES6学习笔记-module(4)
  2. C++中定义类的对象:用new和不用new的区别
  3. 三维重建【四】-------------------结构光 三维重建----论文调研
  4. WinDbg安装与使用
  5. HTTP协议之Chunked解析
  6. DataGrid列操作
  7. 带闰年判断的正则表达式
  8. 互斥量、读写锁长占时分析的利器——valgrind的DRD
  9. C++11容器中新增加的emplace相关函数的使用
  10. OpenCV中Mat数据结构使用举例