Java中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 "":(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 "":(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 valueOf(Class 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就会抱怨找不到与某个名字对应的枚举对象了。另外,在远程方法调用过程中,如果我们发布的客户端接口返回值中使用了枚举类型,那么服务端在升级过程中就需要特别注意。如果在接口的返回结果的枚举类型中添加了新的枚举值,那就会导致仍然在使用老的客户端的那些应用出现调用失败的情况。因此,针对以上两种情况,应该尽量避免使用枚举,如果实在要用,也需要仔细设计,因为一旦用了枚举,有可能会给后期维护带来隐患。
Java中Enum类型的序列化(转)的更多相关文章
- java中enum类型的使用
java 枚举类型enum 的使用 最近跟同事讨论问题的时候,突然同事提到我们为什么java 中定义的常量值不采用enmu 枚举类型,而采用public final static 类型来定义呢?以前我 ...
- JAVA中enum的常见用法
JAVA中enum的常见用法包括:定义并添加方法.switch.遍历.EnumSet.EnumMap 1.定义enum并添加或覆盖方法 public Interface Behaviour{ void ...
- java中可定制的序列化过程 writeObject与readObject
来源于:[http://bluepopopo.iteye.com/blog/486548] 什么是writeObject 和readObject?可定制的序列化过程 这篇文章很直接,简单易懂.尝试着翻 ...
- MYSQL中 ENUM 类型
MYSQL中 ENUM 类型的详细解释 ENUM类型 ENUM 是一个字符串对象,其值通常选自一个允许值列表中,该列表在表创建时的列规格说明中被明确地列举. 在下列某些情况下,值也可以是空串(&quo ...
- Java中double类型的数据精确到小数点后两位
Java中double类型的数据精确到小数点后两位 多余位四舍五入,四种方法 一: double f = 111231.5585;BigDecimal b = new BigDecimal(f); d ...
- java中基本类型封装对象所占内存的大小(转)
这是一个程序,java中没有现成的sizeof的实现,原因主要是java中的基本数据类型的大小都是固定的,所以看上去没有必要用sizeof这个关键字. 实现的想法是这样的:java.lang.Runt ...
- Java进阶(二十三)java中long类型转换为int类型
java中long类型转换为int类型 由int类型转换为long类型是向上转换,可以直接进行隐式转换,但由long类型转换为int类型是向下转换,可能会出现数据溢出情况: 主要以下几种转换方法,供参 ...
- JAVA中值类型和引用类型的不同(面试常考)
转载:https://www.cnblogs.com/1ming/p/5227944.html 1. JAVA中值类型和引用类型的不同? [定义] 引用类型表示你操作的数据是同一个,也就是说当你传一个 ...
- Java中String类型细节
Java中String类型细节 一 . String两种初始化方式 1 . String str1= “abc”;//String类特有的创建字符对象的方式,更高效 在字符串缓冲区中检测”abc”是否 ...
随机推荐
- css3制作惊艳hover切换效果
css3制作经验hover切换效果 <!DOCTYPE html><html> <head> <meta charset="UTF-8" ...
- 1238. Folding
http://acm.timus.ru/problem.aspx?space=1&num=1238 DP+记忆化搜索 思路不难,关键是最优结果的储存问题,为了编写方便,直接用string储存最 ...
- 【转】request和response的页面跳转传参
下面是一位园友的文章: jsp或Servlet都会用到页面跳转,可以用 request.getRequestDispatcher("p3.jsp").forward(request ...
- ubuntu fix the grub boot(need Internet)
sudo add-apt-repository ppa:yannubuntu/boot-repair sudo apt-get update sudo apt-get install -y boot- ...
- 对比学习UIKit和AppKit -- ViewController
在iOS中ViewController的基类是UIViewController:Mac中ViewController的基类是NSViewController. Mac中ViewController父类 ...
- Android重要控件———ListView
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools= ...
- Java与数据库之间时间的处理
Java与数据库之间时间的处理 在数据库中建表: DROP TABLE IF EXISTS `times`; CREATE TABLE `times` ( `id` int(11) NOT NULL ...
- Day9 summary
昨天又翻出收藏夹里一个叫“谷子粒”的bloghttp://1.guzili.sinaapp.com/?p=128#more-128,链接是博主整理的机器学习方面的热点微博,相当的干货.要说我是从知乎对 ...
- Android刷新Dialog
在编写数独游戏时遇到一个问题,当我一次游戏成功后会弹出一个dialog,告诉玩家当前的游戏难度,积分和所用时间,我在onCreateDialog中setMessage之后发现内容一直是不变的,后来找到 ...
- Maven管理 划分模块
转载地址:juvenshun.iteye.com/blog/305865 “分天下为三十六郡,郡置守,尉,监” —— <史记·秦始皇本纪> 所有用Maven管理的真实的项目都应该是分模块的 ...