所有文章已迁移至csdn,csdn个人主页https://blog.csdn.net/chaitoudaren
前言
下面这句代码,相信大家可能跟我一样都非常熟悉,但是真正了解的人又有多少呢?
private static final long serialVersionUID = 1L;
序列化是什么
内存中的数据对象只有转换为二进制流才能进行数据持久化和网络传输。
- 序列化:将数据对象转换为二进制的过程称之为序列化(Serialization)
- 反序列化:将二进制流恢复为数据对象称之为反序列化(Deserialization)
序列化方式
Java原生序列化
Java类通过实现Serializable
接口实现该类对象的序列化
。Java序列化保留了对象类的元数据(类、继承类等),以及对象数据,兼容性好,不支持跨语言,性能一般。
// 序列化
out.writeObject(person);
// 反序列化
(Person)in.readObject();
实现Serializable
接口的类一定要设置serialVersionUID
。如果不设置,每次运行编译器会根据类名、包名、方法、属性等信息自动生成serialVersionUID
。因此一旦修改源代码重新编译serialVersionUID
就可能发生变化,更别提在跨系统之间的传输了。其实反序列化有一个很重要的作用便是进行代码升级控制。
- 当代码进行兼容性升级,不需要修改
serialVersionUID
,这样其他系统反序列化就不会出现失败 - 当代码进行不兼容性升级,也就是说其他接收方也必须对应进行代码升级时,则需要修改
serialVersionUID
。如果此时不修改serialVersionUID
,其他系统又没进行代码升级,这时候接收到的数据就会出现混乱,也就是接收到脏数据
却没被发现。如果此时修改了serialVersionUID
,其他系统没进行升级,此时这会报错,其他系统便会进行排查。报错总比接收脏数据
要强的多。
对照上述两种情况,这里进行一下总结,假设A端进行序列化,通过网络传输给B端进行反序列化,则可能又一下几种情况:
- A端B端的
serialVersionUID
不同,则报错
Exception in thread "main" java.io.InvalidClassException: com.cmx.code.serial.Person; local class incompatible: stream classdesc serialVersionUID = 1234567890, local class serialVersionUID = 123456789
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:621)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.sf.code.serial.DeserialTest.main(DeserialTest.java:13)
- serialVersion相同,A端比B端多一个字段,则B端反序列化会丢失字段信息
- serialVersion相同,A端比B端少一个字段,则B端无法接收到对应字段信息,设置为默认值,如果是int等基础类型则为0
- 静态变量不会被序列化,序列化保存的是对象的状态,静态变量属于类的状态,所以静态变量不会被序列化传输
- 反序列化不会调用类的
无参构造器
方法,而是调用native
方法
Hessian序列化
Hession序列化是一种支持动态类型、跨语言、基于对象传输的网络协议。可以在多语言下进行反序列化(C++、Python),具有以下特点:
- 占用空间小
- 跨语言
- 比Java原生序列化高效
JSON序列化
FastJson应该是当下最流行的json工具,也是阿里开源的一款产品。使用也非常简单:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
// 序列化
JSON.toJSONString(person);
// 反序列化
JSON.parseObject(json, Person.class);
使用Json进行序列化最大的好处就是可读性高,但是也带来了一些问题:
- Json在序列化时并没有带上
类型信息
,因此反序列化使用可能出现不准确的情况。如String类型的0被反序列化成int类型的0 - 可读性高也代表着信息很容易泄露,因此涉及敏感数据时应该使用
transient
关键字,使其不被序列化到Json
中。如果一定需要进行序列化
的,也应该使用密钥进行加密
- 无论是fastjson、jackson都曾出现过
恶意构造Json串
,使Json在反序列化时执行恶意代码
的漏洞,因此如果使用Json进行序列化,应尽量保持与官方版本同步
,防止漏洞攻击