作者ChihMinh,原作链接https://chihminh.github.io/2016/08/10/serializable/,转载请注明出处
什么是序列化?
将对象编码为字节流,该字节流能描述对象的类、版本信息、内部状态。最重要的一点就是对象的内部状态,即对象的所有fields,但不包括transient和static修饰的域。一言以蔽之:序列化即以某种媒介存储(以字节流的形式)对象的状态信息,反序列化即从字节流恢复状态信息。
为什么要序列化?
参考内容原文见这里
- Communication: If you have two machines that are running the same code, and they need to communicate, an easy way is for one machine to build an object with information that it would like to transmit, and then serialize that object to the other machine. It’s not the best method for communication, but it gets the job done.
- Persistence: If you want to store the state of a particular operation in a database, it can be easily serialized to a byte array, and stored in the database for later retrieval.
- Deep Copy: If you need an exact replica of an Object, and don’t want to go to the trouble of writing your own specialized clone() class, simply serializing the object to a byte array, and then de-serializing it to another object achieves this goal.
- Caching: Really just an application of the above, but sometimes an object takes 10 minutes to build, but would only take 10 seconds to de-serialize. So, rather than hold onto the giant object in memory, just cache it out to a file via serialization, and read it in later when it’s needed.
- Cross JVM Synchronization: Serialization works across different JVMs that may be running on different architectures.
- 通信:如果两台机器运行相同的代码需要进行通信,可以把一个对象序列化然后传送到另一台机器,尽管这不是通信的最好方法。
- 持久化:将对象保存到文件中或者数据库中。
- 深复制:不需要自己重写Object.clone()方法,只需要序列化和反序列化就能做到
- 缓存:如果生成某个对象很费时,而反序列化又很快,那么完全可以将一个对象序列化存到本地文件,在需要的时候进行反序列化。
- 跨JVM同步:序列化与反序列化都是JVM独立的,也就是可以在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。
总结为:将对象编码为字节流写进文件或者在不同的JVM之间传输。
怎样序列化?
一个类实现Serializable接口即可序列化,Serializable接口没有任何变量和方法
static及transicnt修饰的变量
static
|
|
运行结果:
可以看到反序列化之后的static变量已经改变,transient关键字修饰的变为默认的null。事实上就是序列化过程中不会保存static和transient修饰的变量的值,当然也不会保存访问修饰符(public, private, protected, 默认)信息。
序列化与继承关系
子类实现Serializable接口
根据Serializable规范【Java SE 8】
To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype’s public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class’s state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime.
那么分两种情况:
1. 父类没有实现Serializable接口
那么序列化子类时,父类有、子类没有的属性不会被写到字节流中去,对应地反序列化时对这些属性不能从字节流中读取出来,只能(隐式)调用父类的无参构造器对这些属性初始化。这就要求序列化子类的未序列化父类必须要有无参构造器。
父类
子类
序列化与反序列化
输出结果
可以看到父类所有属性都是通过调用无参构造器初始化的。
注释掉子类和父类的无参构造器,再运行程序会出现下面异常。最终问题出在“Sub sub1 = (Sub)ois.readObject()”,反序列化的时候。
2. 父类实现了Serializable接口
使上述代码中的Super类实现Serializable接口,则结果如下:
如果父类也实现了Serializable接口,那么序列化子类时会把父类的所有属性(包括私有属性)全都序列化,父类有没有无参构造器都没有关系。
父类实现Serializable接口,子类没有
根据Serializable规范【Java SE 8】
Serializability of a class is enabled by the class implementing the java.io.Serializable interface. Classes that do not implement this interface will not have any of their state serialized or deserialized. All subtypes of a serializable class are themselves serializable. The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.
所有可序列化类的子类都是可序列化的,包括父类中没有的属性。
将上述代码中的子类和父类是否实现Serializable接口互换一下,结果如下: