001-基础知识专题--关键字和接口-1、Serializable接口
1. 系列化的用途
- 想把的内存中的对象状态保存到一个文件中或者数据库中时候
- 网络通信时需要用套接字在网络中传送对象时,如我们使用 RPC 协议进行网络通信时
2. 如何序列化
只要一个类实现 Serializable 接口,那么这个类就可以序列化了。
3. 关于 serialVersionUID
在反序列化的过程中则需要使用 serialVersionUID 来确定由那个类来加载这个对象,所以我们在实现 Serializable 接口的时候,一般还会要去尽量显示地定义 serialVersionUID,如:
1 |
|
在反序列化的过程中,如果接收方为对象加载了一个类,如果该对象的 serialVersionUID 与对应持久化时的类不同,那么反序列化的过程中将会导致 InvalidClassException 异常。例如,在之前反序列化的例子中,我们故意将 User 类的 serialVersionUID 改为 2L,如:
1 |
|
那么此时,在反序例化时就会导致异常,如下:
1 |
|
如果我们在序列化中没有显示地声明 serialVersionUID,则序列化运行时将会根据该类的各个方面计算该类默认的 serialVersionUID 值。但是,Java 官方强烈建议所有要序列化的类都显示地声明 serialVersionUID 字段,因为 如果高度依赖于JVM默认生成serialVersionUID
,可能会导致其与编译器的实现细节耦合,这样可能会导致在反序列化的过程中发生意外的 InvalidClassException 异常。因此,为了保证跨不同Java编译器实现的serialVersionUID值的一致,实现Serializable接口的必须显示地声明serialVersionUID字段
。
4. 静态变量序列化
串行化只能保存对象的非静态成员交量,不能保存任何的成员方法和静态的成员变量,而且串行化保存的只是变量的值,对于变量的任何修饰符都不能保存。
如果把 Person 类中的 name 定义为 static 类型的话,试图重构,就不能得到原来的值,只能得到 null。说明 对静态成员变量值是不保存的
。这其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此序列化并不保存静态变量。
5. transient
当某些变量不想被序列化,同是又不适合使用 static 关键字声明,那么此时就需要用 transient 关键字来声明该变量。
[[../../../../cubox/006-ChromeCapture/20221024-Java transient 关键字使用小记 - Alexia(minmin) - 博客园||Java transient 关键字]]
对象的序列化可以通过实现两种接口来实现,若实现的是 Serializable 接口,则所有的序列化将会自动进行,若实现的是 Externalizable 接口,则没有任何东西可以自动序列化,需要在 writeExternal 方法中进行手工指定所要序列化的变量,这与是否被 transient 修饰无关。
6. 序列化中的问题
- 一个子类实现了 Serializable 接口,它的父类都没有实现 Serializable 接口,序列化该子类对象。要想反序列化后输出父类定义的某变量的数值,就需要让父类也实现 Serializable 接口或者父类有默认的无参的构造函数。
- 在父类没有实现 Serializable 接口时,虚拟机是不会序列化父对象的,而一个 Java 对象的构造必须先有父对象,才有子对象,反序列化也不例外。所以反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取父对象的变量值时,它的值是调用父类无参构造函数后的值,如果在父类无参构造函数中没有对变量赋值,那么父类成员变量值都是默认值,如这里的 Long 型就是 null。
- 根据以上特性,我们可以将不需要被序列化的字段抽取出来放到父类中,子类实现 Serializable 接口,父类不实现 Serializable 接口但提供一个空构造方法,则父类的字段数据将不被序列化。
- serialVersionUID 字段地声明要尽可能使用 private 关键字修饰,这是因为该字段的声明只适用于声明的类,该字段作为成员变量被子类继承是没有用处的!
- 数组类是不能显示地声明 serialVersionUID 的,因为它们始终具有默认计算的值,不过数组类反序列化过程中也是放弃了匹配 serialVersionUID 值的要求。
- 当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化,那么这个引用对象必须是可序列化的,这个类才能序列化。
6.1. 同一个对象多次序列化之间有属性更新,前后的序列化有什么区别?
结论:当对象第一次序列化成功后,后续这个对象属性即使有修改,也不会对后面的序列化造成成影响。
原因:是序列化算法的原因,所有要序列化的对象都有一个序列化的编码号,当试图序列化一个对象,会检查这个对象是否已经序列化过,若从未序列化过,才会序列化为字节序列去输出。若已经序列化过,则会输出一个编码符号,不会重复序列化一个对象。如下
7. 参考
[[../../../../cubox/006-ChromeCapture/20221026-谈谈我对Serializable接口的理解 - 掘金]]