`
MauerSu
  • 浏览: 519546 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Java中Enum类型的序列化

 
阅读更多
源:http://mysun.iteye.com/blog/1581119
评:

大概意思就是说,在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中
enum内部变量 不能被序列化进去


在Java中,对Enum类型的序列化与其他对象类型的序列化有所不同,今天就来看看到底有什么不同。下面先来看下在Java中,我们定义的Enum在被编译之后是长成什么样子的。
Java代码:
Java代码  收藏代码
public enum FruitEnum { 
    APPLE, ORAGE 


上面的代码定义了一个FruitEnum类型,是最简单形式的,下面我们来看看编译之后的字节码。
字节码:
Java代码  收藏代码
public final class com.taobao.tianxiao.FruitEnum extends java.lang.Enum 
.... 
.... 
.... 

public static final com.taobao.tianxiao.FruitEnum APPLE; 
 
public static final com.taobao.tianxiao.FruitEnum ORAGE; 
 
static {}; 
  Code: 
   Stack=4, Locals=0, Args_size=0 
   0:   new #1; //class com/taobao/tianxiao/FruitEnum 
   3:   dup 
   4:   ldc #13; //String APPLE 
   6:   iconst_0 
   7:   invokespecial   #14; //Method "<init>":(Ljava/lang/String;I)V 
   10:  putstatic   #18; //Field APPLE:Lcom/taobao/tianxiao/FruitEnum; 
   13:  new #1; //class com/taobao/tianxiao/FruitEnum 
   16:  dup 
   17:  ldc #20; //String ORAGE 
   19:  iconst_1 
   20:  invokespecial   #14; //Method "<init>":(Ljava/lang/String;I)V 
   23:  putstatic   #21; //Field ORAGE:Lcom/taobao/tianxiao/FruitEnum; 
   26:  iconst_2 
   27:  anewarray   #1; //class com/taobao/tianxiao/FruitEnum 
   30:  dup 
   31:  iconst_0 
   32:  getstatic   #18; //Field APPLE:Lcom/taobao/tianxiao/FruitEnum; 
   35:  aastore 
   36:  dup 
   37:  iconst_1 
   38:  getstatic   #21; //Field ORAGE:Lcom/taobao/tianxiao/FruitEnum; 
   41:  aastore 
   42:  putstatic   #23; //Field ENUM$VALUES:[Lcom/taobao/tianxiao/FruitEnum; 
   45:  return 
  LineNumberTable:  
   line 4: 0 
   line 3: 26 
 
public static com.taobao.tianxiao.FruitEnum[] values(); 
  Code: 
   Stack=5, Locals=3, Args_size=0 
   0:   getstatic   #23; //Field ENUM$VALUES:[Lcom/taobao/tianxiao/FruitEnum; 
   3:   dup 
   4:   astore_0 
   5:   iconst_0 
   6:   aload_0 
   7:   arraylength 
   8:   dup 
   9:   istore_1 
   10:  anewarray   #1; //class com/taobao/tianxiao/FruitEnum 
   13:  dup 
   14:  astore_2 
   15:  iconst_0 
   16:  iload_1 
   17:  invokestatic    #31; //Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V 
   20:  aload_2 
   21:  areturn 
  LineNumberTable:  
   line 1: 0 
 
public static com.taobao.tianxiao.FruitEnum valueOf(java.lang.String); 
  Code: 
   Stack=2, Locals=1, Args_size=1 
   0:   ldc #1; //class com/taobao/tianxiao/FruitEnum 
   2:   aload_0 
   3:   invokestatic    #39; //Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 
   6:   checkcast   #1; //class com/taobao/tianxiao/FruitEnum 
   9:   areturn 
  LineNumberTable:  
   line 1: 0 


上面的字节码已经去掉的常量池部分,但是即便如此,在我们的源代码中如此简单的一个FruitEnum类,编译器居然为我们产生了这么多的字节码,哇哦~~~~~~~~
仔细地看这段代码, 编译器是在为我们创建一个类,这个类继承自 java.lang.Enum ,有两个公共的、静态的、被声明成final的属性,它们的类型就是我们定义的FruitEnum。同时,编译器还生成了一个静态初始话器,就是字节码中static{};这一行下面的代码,其中的字节码创建了两个FruitEnum对象,同时分别赋值给APPLE和ORANGE这两个属性,调用的构造函数是定义在 java.lang.Enum中的protected Enum(String name, int ordinal)方法。在创建完成两个FruitEnum对象并且分别赋值给APPLE和ORIGIN之后,还创建了一个名叫ENUM$VALUES的数组,然后把APPLE和ORIGIN按照定义的顺序放如这个数组中。
除了这个静态初始化器之外,编译器还为我们生成了两个静态方法,values()和 valueOf(java.lang.String)方法。其中values()方法将ENUM$VALUES数组拷贝一份然后返回,而valueOf(java.lang.String)方法则会调用java.lang.Enum类中的valueOf方法,其作用是根据参数名找到对应的具体的枚举对象,如果找不到的话会抛出一个IllegalArgumentException异常。
从上面的叙述可以看到,我们定义的枚举类型,经过编译器的处理最终会编程一个对象的定义,其中的枚举变量其实就是类的静态变量,因此Java中的枚举类型其实是具有很多对象的特性的,只不过平时我们都不太用到,比如枚举可以实现接口(不能继承)、定义方法等等。为了保证枚举类型像Java规范中所说的那样,每一个枚举类型极其定义的枚举变量在JVM中都是唯一的,在枚举类型的序列化和反序列化上,Java做了特殊的规定。原文如下(摘自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方法长什么样子。
java代码:
Java代码  收藏代码
public static <T extends Enum<T>> T valueOf(Class<T> enumType, 
                                                String name) { 
        T result = enumType.enumConstantDirectory().get(name); 
        if (result != null) 
            return result; 
        if (name == null) 
            throw new NullPointerException("Name is null"); 
        throw new IllegalArgumentException( 
            "No enum const " + enumType +"." + name); 
    } 

从代码中可以看到,代码会尝试从调用enumType这个Class对象的enumConstantDirectory()方法返回的map中获取名字为name的枚举对象,如果不存在就会抛出异常。再进一步跟到enumConstantDirectory()方法,就会发现到最后会以反射的方式调用enumType这个类型的values()静态方法,也就是上面我们看到的编译器为我们创建的那个方法,然后用返回结果填充enumType这个Class对象中的enumConstantDirectory属性。
在了解了Java如何处理枚举的定义以及序列化和反序列化枚举类型之后,我们就需要在系统或者类库升级时,对其中定义的枚举类型多加注意,为了保持代码上的兼容性,如果我们定义的枚举类型有可能会被序列化保存(放到文件中、保存到数据库中,进入分布式内存缓存中),那么我们是不能够删除原来枚举类型中定义的任何枚举对象的,否则程序在运行过程中,JVM就会抱怨找不到与某个名字对应的枚举对象了。另外,在远程方法调用过程中,如果我们发布的客户端接口返回值中使用了枚举类型,那么服务端在升级过程中就需要特别注意。如果在接口的返回结果的枚举类型中添加了新的枚举值,那就会导致仍然在使用老的客户端的那些应用出现调用失败的情况。因此,针对以上两种情况,应该尽量避免使用枚举,如果实在要用,也需要仔细设计,因为一旦用了枚举,有可能会给后期维护带来隐患。
分享到:
评论

相关推荐

    C语言基础语法与程序设计入门讲解

    c语言入门 C语言一经出现就以其功能丰富、表达能力强、灵活方便、应用面广等特点迅速在全世界普及和推广。C语言不但执行效率高而且可移植性好,可以用来开发应用软件、驱动、操作系统等。C语言也是其它众多高级语言的鼻祖语言,所以说学习C语言是进入编程世界的必修课。hello,world #include<stdio.h>  int main() {     /*在双引号中间输入Hello World*/      printf("Hello World");     return 0;  } 注:在最新的C标准中,main函数前的类型为int而不是void c语言的具体结构简单来说,一个C程序就是由若干头文件和函数组成。#include <stdio.h>就是一条预处理命令, 它的作用是通知C语言编译系统在对C程序进行正式编译之前需做一些预处理工作。函数就是实现代码逻辑的一个小的单元。必不可少之主函数一个C程序有且只有一个主函数,即main函数。C程序就是执行主函数里的代码,也可以说这个主函数就是C语言中的唯一入口。而main前面的int就是主函数的类型.printf()是格式输出函数,这里

    部门绩效考核表模板(基于KPI,以月度为例1).xlsx

    部门绩效考核表模板(基于KPI,以月度为例1)

    基于YOLOv5的移动机器人动态视觉SLAM算法研究.pdf

    基于YOLOv5的移动机器人动态视觉SLAM算法研究.pdf

    基于二阶锥优化的电气综合能源系统协调调度策略研究与仿真-利用MATLAB及CPLEX平台精准求解,MATLAB代码:基于二阶锥优化电气综合能源系统优化调度研究 关键词:电气综合能源 优化调度 二阶锥

    基于二阶锥优化的电气综合能源系统协调调度策略研究与仿真——利用MATLAB及CPLEX平台精准求解,MATLAB代码:基于二阶锥优化电气综合能源系统优化调度研究 关键词:电气综合能源 优化调度 二阶锥优化 参考文档:《考虑气电联合需求响应的气电综合能源配网系统协调优化运行_刘天琪》参考部分配电网设备模型,非完全复现,具体以店主自写文档为准 仿真平台:MATLAB+CPLEX 平台 优势:代码具有一定的深度和创新性,注释清晰,非烂大街的代码,非常精品 主要内容:代码主要做的是电气综合能源系统的优化调度策略,气网部分和电网部分的相关约束都通过二阶锥或者其他线性化的方法进行化简,模型清晰且容易求解,经过化简后采用CPLEX实现求解,可以在此基础上扩加储能、SVG、OLTC以及电容器等相关设备,升级版的程序店主也有,该代码适合新手学习以及在此基础上进行拓展,代码质量非常高,保姆级的注释以及人性化的模块子程序,所有数据均有可靠来源 ,基于二阶锥优化的电气综合能源系统优化调度MATLAB代码研究

    春节文化主题活动策划与作文素材集锦

    春节主题作文素材 初中语文主题学习 2025年01月21日 20:26 山西 写年味 开头:新年的脚步渐近,街头巷尾瞬间被年味填满。大红灯笼高高挂起,恰似熟透的红柿子,在风中轻晃,透着喜庆。街边店铺张贴着崭新春联,墨香在空气中氤氲。年货摊前人头攒动,吆喝声、谈笑声交织。孩子们手持糖葫芦,欢笑着穿梭其中,那鲜艳糖衣与红彤彤脸蛋相映,年味愈发浓郁,似要将寒冬暖化 。 结尾:置身这浓烈年味里,我沉醉不已。它是团圆温馨,是对过往的怀念、对未来的期许。当烟花在夜空绽放,那光亮如同希望之火。愿这份年味永不消散,伴我们走过岁岁年年,让温暖与幸福在生活中延续 。 满分作文题目: 《灯火映新岁,年味满人间》 《街头巷尾,年味悠长》 《大红灯笼,摇曳年味时光》 《墨香春联,晕染年味画卷》 《糖葫芦串起的年味》 开头:记忆深处,年味是外婆手中的剪纸。每至腊月,外婆便戴上老花镜,坐在暖阳下,手中红纸在剪刀下翻转,不一会儿,栩栩如生的花鸟鱼虫便跃然纸上。她脸上慈祥笑容,和着剪纸独特艺术魅力,成为我对年味最初印象。如今,外婆已年迈,可那份年味记忆,从未淡去 。 结尾:重拾外婆剪纸,往昔春节场景浮现

    dbeaver离线安装版 (数据库管理工具dbeaver-ce-23离线驱动+安装包+使用说明) 内置sqlserver、mysql、oracle、opengauss数据库 可满足日常使用

    dbeaver离线安装版。(数据库管理工具dbeaver-ce-23离线驱动+安装包+使用说明) 内置sqlserver、mysql、oracle、opengauss数据库。可满足日常使用。

    基于LEBERT-CRF和知识图谱的中文地址修正补全方法.pdf

    基于LEBERT-CRF和知识图谱的中文地址修正补全方法.pdf

    (最新!!)1997-2022年《中国教育经费统计年鉴》合集

    比较全面、系统地反映了历年全国教育经费来源和使用的情况,为国家和地方编制教育发展规划制定教育财政政策提供了重要的参考依据。它对于研究教育经费结构和使用效益有一定价值对于各地之间的情况交流,提高教育财务管理水平,也将会起到促进作用。 全国教育经费统计资料的各项数据是从最基层单位开始填报,经过乡(镇)、县(市、区)、地(市)、省(自治区、直辖市)等教育主管部门层层汇总的。

    apache-commons-lang-javadoc-2.6-15.el7.x64-86.rpm.tar.gz

    1、文件内容:apache-commons-lang-javadoc-2.6-15.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/apache-commons-lang-javadoc-2.6-15.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装

    基于向量特征的车辆轨迹预测.pdf

    基于向量特征的车辆轨迹预测.pdf

    apache-rat-plugin-0.8-13.el7.x64-86.rpm.tar.gz

    1、文件内容:apache-rat-plugin-0.8-13.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/apache-rat-plugin-0.8-13.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装

    2023年全国大学生英语竞赛样题(C类).pdf

    2023年全国大学生英语竞赛样题(C类)

    惯导里程计GPS组合导航算法下的卡尔曼滤波matlab代码实现与性能优化,惯导里程计GPS组合导航算法,matlab代码卡尔曼滤波 ,核心关键词:惯导里程计; GPS组合导航算法; 导航算法; MAT

    惯导里程计GPS组合导航算法下的卡尔曼滤波matlab代码实现与性能优化,惯导里程计GPS组合导航算法,matlab代码卡尔曼滤波 ,核心关键词:惯导里程计; GPS组合导航算法; 导航算法; MATLAB代码; 卡尔曼滤波。,"基于Matlab的惯导里程计与GPS组合导航算法的卡尔曼滤波实现"

    2023年全国大学生英语竞赛样题(C类)样题答案及听力原文.pdf

    2023年全国大学生英语竞赛样题(C类)样题答案及听力原文

    ant-apache-log4j-1.9.4-2.el7.x64-86.rpm.tar.gz

    1、文件内容:ant-apache-log4j-1.9.4-2.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/ant-apache-log4j-1.9.4-2.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装

    44 -生产车间主任绩效考核表1.xlsx

    44 -生产车间主任绩效考核表1

    aarch64交叉编译的valgrind-3.24.0版本

    交叉编译工具:aarch64-linux valgrind软件版本:3.24.0 使用时添加环境变量: export VALGRIND_LIB=/opt/aarch64_build/libexec/valgrind export PATH="/opt/aarch64_build/bin":$PATH

    01-【标准制度】绩效考核体系(附全套流程).docx

    01-【标准制度】绩效考核体系(附全套流程)

    酒店前厅客房主管晋升考核表.xlsx

    酒店前厅客房主管晋升考核表

Global site tag (gtag.js) - Google Analytics