Serializable

Serializable

通过实现 java.io.Serializable 可以让 class 实现序列化的功能,否则无法使用序列化的功能。一个实现序列化的类的子类都是可以进行序列化的。Serializable 接口没有方法,字段等只是一个标记它是可以序列化的作用。

序列化是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或在网络中传送),并在需要的时候恢复原先状态的过程。

当子类实现了 Serializable 接口而父类没有实现的时候,父类要有一个可以访问的无参构造函数。

在反序列化的时候,没有实现序列化的类字段将会使用 public 或者 protect 的无参构造函数进行初始化。无参构造函数必须是实现序列化的子类可访问的,

如果需要在序列化和反序列化的时候进行一些特殊操作的话,需要自己实现以下某些方法:

1
2
3
4
5
6
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
private void readObjectNoData()
throws ObjectStreamException;

writeObject 方法负责写入指定的类的对象状态来让 readObject 方法可以读取这些状态。可以通过调用 out.defaultWriteObject 方法来使用默认的保存对象字段的机制。

readObject 方法负责从流中读取并回复类字段。它可能会调用 in.defaultReadObject 方法来默认恢复非静态和非临时(transient)的字段机制。defaultReadObject 方法使用流中的信息来分配流中保存的对象字段到目前的对象相应名字的字段中。这解决了当类已经添加了新的字段时的问题。

这两个方法不需要考虑它自己的父类或者子类。状态是通过 writeObject 方法将各自的字段写入到 ObjectOutputStream 中,或者通过 DataOutput 提供的方法将私有的数据类型进行保存。

readObjectNoData 方法负责在当序列化流没有列出指定的类作为反序列化的对象的父类的时候初始化对象的状态。这可能会发生在接收部分使用了一个和发送部分不同版本的反序列化实例的类的时候,还有接收的版本继承的类不是发送版本继承的类的时候。还可能发生在序列化流发生错误的时候。因此,readObjectNoData 方法在”恶意”的或者不完整的流情况下初始化反序列化对象属性是非常有用的。

Serializable 类需要在写入一个对象到流的时候指定一个替换的对象的话需要用指定的修饰符实现这个方法

1
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

这个 writeReplace 方法如果存在并且它可以被被序列化的对象中定义的方法调用的话,writeReplace 会在序列化的时候被调用。因此,这个方法可以使用 private , protected 或者 package-private修饰。子类的访问权限遵循 Java 的访问规则。

如果类的实例从流中读取出来的时候需要指定一个替换的类的话应该使用明确的修饰符实现这个方法。

1
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

readResolve 方法的调用规则和访问规则和 writeReplace 相同。

序列化在运行时会关联每一个可以序列化的类的版本数字,名为 serialVersionUID , 这个 serialVersionUID 会在反序列化的时候使用,主要作用是验证一个序列化对象的发送方和接收方加载的类是兼容的。如果接收方给对象加载的类的 serialVersionUID 和对应的 发送方的类的 serialVersionUID 不同的话,反序列化的时候会导致一个 InvalidClassException . 可序列化的类可以通过定义一个静态 final long 字段名为 “serialVersionUID” 声明它自己的 serialVersionUID :

1
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

如果可序列化的类没有明确的定义一个 serialVersionUID ,那么运行时序列化将会如Java(TM)对象序列化规范所描述的那样依据类的多个方面计算出一个默认的 serialVersionUID 值。尽管如此,官方还是强烈推荐所有的可序列化类明确的定义 serialVersionUID 值,因为默认的 serialVersionUID 计算对类的细节是高度敏感的,会依据编译器的实现而变动,可能会在反序列化时导致预期之外的 InvalidClassException 异常。因此,为了确保在不同的 Java 编译器实现中保持 serialVersionUID 不变,可序列化的类必须明确定义一个 serialVersionUID 值。官方还强烈推荐尽可能使用 private 来修饰 serialVersionUID 定义,因为这样的声明只适用于当前的类,而不会影响那些继承他的类。Array 类不能显式的声明 serialVersionUID ,所以它们总是使用默认计算的值,但是 Array 类不需要比对 serialVersionUID 。

Android 实现的 serialVersionUID 计算方式在 Android N 上会有一些轻微的不同。为了保持兼容性,这个改变只有在应用的目标 SDK 版本设置为 24 或者更高的时候才会可用。强烈推荐使用显式的 serialVersionUID 字段来避免兼容性问题。

谨慎的实现 Serializable
参考 《Effective Java》中对于序列化 API 覆盖的章节。书中介绍了如何在不影响应用程序的可维护性的前提下使用这个接口。

推荐另一个可行的方法
JSON 是一种简明,可读性高并且高效的方式。Android 包含了 android.util.JsonReader 流 API 和 org.json.JSONObject 树 API 来对 JSON 进行读写。也可以使用绑定库比如 GSON 来更直接的读写 Java 对象。

Reference

https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html

About Me

我的博客 leonchen1024.com

我的 GitHub https://github.com/LeonChen1024

微信公众号

wechat

You forgot to set the business and currency_code for Paypal. Please set it in _config.yml.
You forgot to set the url Patreon. Please set it in _config.yml.
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×