1. 序列化基本原理
- 序列化:对象—> IO
- 反序列化:IO —>对象
- 意义:序列化将 Java 对象转换位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输(例如 rpc )再反序列之后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。
- 使用原理:序列化一个对象,先创建 OutputStream (如 FileOutputStream ),然后将 OutputStream 封装在ObjectOutputStream 中,最后调用 writeObject 方法序列化;反序列化需要将 InputStream (如FileInputstream )封装在 ObjectInputStream 内,最后调用 readObject 方法。
如果想要一个类可序列化,只要实现 Serializable 接口即可。
2. 变量是其他引用类
如果我们要序列化的类的某个变量是其他类,那么作为变量的那个类也必须序列化。
1 | public class User implements Serializable { |
代码中省略了get set 等其他方法。单元测试如下:
1 |
|
执行结果为:
java.io.NotSerializableException:
因为 AppUser 不可序列化,导致 User 也无法序列化。
3. serialVersionUID
在实体类实现 Serializable 接口之后,一般我们会指定 serialVersionUID 。如果不指定会有问题吗?我们来试一下:
1 | public class User implements Serializable { |
执行序列化测试:
1 |
|
执行成功,如果我们现在有需求修改 User 类,新加一个变量,如下:
1 | private String address; |
对应添加 get 、set 方法,然后执行反序列化单元测试:
1 |
|
执行结果为:
java.io.InvalidClassException: local class incompatible: stream classdesc serialVersionUID = 3391156310830289756, local class serialVersionUID = 8094924620444168806
由报错信息可知,虽然我们没有设置 serialVersionUID 的值,但是序列化的时候默认生成了。具体的生成方法我没有研究过,但肯定和类的变量有关。那么,当类的变量发生变化时,序列化功能就会出现问题。我们需要保证它的兼容性。建议每个序列化的类都指定 serialVersionUID。
阿里巴巴 Java 开发手册有如下强制规定:
10.【强制】序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败;如 果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。
4. 同一个对象多次序列化
同一个对象会被序列化多次吗?
1 | public class AppUser implements Serializable { |
单元测试如下:
1 |
|
分别序列化 appUser、user1、user2,其中 user1 序列化了两次,然后按照顺序反序列化。执行结果如下:
false
true
true
true
说明,appUser 从始至终只被序列化了一次,user1 和 user2 中的 appUser 是同一个对象。user1 和 user11 也是反序列化的同一个对象。
结论:同一个对象只会被序列化一次。