在探讨这个问题之前先上个概念。什么是序列化和反序列化呢?
- 序列化就是指把Java对象转换为字节流写入硬盘的过程。
public static void writeObject(Object obj, String dstFilePath) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(dstFilePath));
out.writeObject(obj);
out.close();
}
- 反序列化就是指把硬盘上的二进制文件用字节流读入内存中,恢复为Java对象的过程
public static Object loadObject(String fromFile) throws IOException, ClassNotFoundException {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(fromFile));
Object obj = in.readObject();
in.close();
return obj;
}
了解了序列化和反序列化后,该上菜了?
通过查看源码我们可以看到Serializable是一个空接口,凭什么你肚子里啥都没有,我实现了你,我就可以序列化了?难道是Serializable接口充钱了?很显然这里面肯定有文章。
我们进入ObjectOutputStream类中查看writeObject(Object)方法进行查看,至于为什么要进这个类看,原因是序列化用到了这个方法。
看到了这个方法是不是看半天没看出来啥,看不出来就对了铁子,因为这里做事的另有其人,它就是writeObject0()方法,那我们就应该进入这个方法一探究竟!
看到这相信大家都已经知道了,这里做个简单的说明:instanceof这个关键字呢是用来判断是否是某种类型,那么上图就很好理解了。
JDK中的序列化和反序列化,在进行操作之前,会对类型做检查。只有以下类型才能正常序列化:
- String
- Array
- Enum
- Serializable
所以我们自定义的类型要想实现序列化,必须实现Serializable接口,从而变成Serializable类型。
在这里顺便说个与本文章无关的却被很多人忽视的知识。
serialVersionUID属性。在我们实现序列化接口以后,会有如下警告:提示我们没有声明一个long 类型的 static final 变量 serialVersionUID 。
The serializable class Student does not declare a static final serialVersionUID
field of type long
如果不显式定义serialVersionUID,序列化时会根据当前类的结构自动生成一个序列号,这个序列化跟 类结构有关,如果类的结构有变化这个序列化也会变化。 基于上面的原因,如果我们的类在序列化以后,结构发生了变化(例如添加、删除了属性、方法),会 导致反序列化时类的序列号和当前序列化到硬盘上时的序列号不一致,从而无法正常反序列化(对象迷失在二进制世界中回不来了)。
反序列化时类的序列号和当前序列化到硬盘上时的序列号不一致,从而无法正常反序列化(对象迷失在二进制世界中回不来了)。
重点来了,实现序列化时在类名出现警告时,建议显式定义一下UID。