【强制】当序列化类新增属性时,请不要修改serialVersionUID字段,以免反序列化失败,如果完全不兼容升级,避免反序列化混乱,那么请修改serialVersionUID的值。
说明:注意serialVersionUID值不一样会抛出序列化运行时异常。
序列化和反序列化是什么?为什么需要它?
序列化是将内存中的对象信息转化成可以存储或传输的数据到临时或永久存储的过程。而反序列化正好相反,是从永久或临时存储中读区序列化的数据并转化成内存对象的过程。
序列化:对象数据-->字节流
反序列化:字节流-->对象数据
为什么需要序列化和反序列化呢?
大家可以回忆一下,平时如果将文字文件、图片文件、视频文件案件安装包等传递给小伙伴时,这些资源在计算机中的存储方式是怎样的?进而再思考,Java中的对象如果需要传输应该通过什么形式呢?
我们都知道,一个文件通常是m个字节的序列:B0、B1、B2...Bm-1,所有的I/O设备都被模型化为文件,而所有的输入和输出都被当作文件的读和写来执行。
大家设想一个典型的场景:如果实现Java远程方法调用,如果调用方和服务方不在同一台机器上就很难进行共享内存,所以只能通过网络进行传输。而想要将Java中的对象进行网络传输或者存储到文件中,就需要将Java对象转换成二进制字节流,这就是序列化。存储或传输后需要将二进制字节流解析成Java对象,这个过程就是反序列化。
序列化的主要目的:方便存储或网络传输。
常见的序列化方式
常见的序列化方式包括:Java原生序列化、Hession序列化、Kryo序列化、JSON序列化等。
Java原生序列化
在学习源码注释之前,我们可以站在设计者的角度思考一个问题:如果一个类序列化到文件之后,类的结构发生改变还能否正确的反序列化?
答案显然是不确定的。
那么如何判断文件被修改过了呢? 通常可以通过加密算法对其进行签名,文件作出任何修改签名就会变得不一致。但是Java序列化的场景并不适用上述的方案,因为在类中加了个空格或换行,类的结构没有发生改变,那类的签名就不应该改变。
那么是否可以通过约定一个唯一ID,通过ID对比,不一致就认为不可反序列化呢?
实现了序列化接口后,如果开发者不手动制定序列化ID怎么办?
Serializable的源码注释特别长,其核心说明:
-
Java原声序列化需要实现Serializable接口,序列化接口不包含任何方法和属性等,只起到序列化标识的作用。
-
一个类实现序列化接口,其子类也继承序列化能力,但实现序列化接口的类中有其他类的引用,其他类也需要实现序列化接口。序列化时如果抛出NotSerializableException,证明该对象没有实现Serializable接口。
-
建议每个类都手动指定serialVersionID,如果不手动指定,编译器会动态的生成默认的序列化号,因为这个默认的序列化号跟类的特征和编译器的实现有关系,很容易在反序列化时抛出InvalidClassException异常。建议将序列化ID声明为私有,避免运行时被修改。(我从来没手动写过序列化版本号,都是默认的,也从来没遇到这个异常,可能是代码写的太少?)
JSON序列化
JSON序列化是一种轻量级的数据交互方式,JSON序列化将对象转换成JSON字符串。常用的JSON序列化的库有Gson、Jackson、Fastjson。