(基于Android sdk 26)
实现了Serializable的接口的类就可以进行序列化、反序列化。反之,未实现Serializable接口的类就不能进行序列化、反序列化。如果一个类实现了Serializable,那么它的所有子类,是可以进行序列化、反序列化的。Serializable接口没有任何属性、方法,只是表明可以进行序列化过程。
为了使子类(没有显示声明Serializable)可以序列化,子类要保存并恢复父类的属性,权限包括public, protected, package。所以子类需要有一个可访问权限的无参构造函数,以便进行类的初始化。如果未满足这个条件,运行时会出异常;
反序列化时,没有实现Serializable的属性将会通过其public, protected的无参构造函数进行初始化。所以无参构造函数必须是子类可访问的。这些是Serializable子类的属性,将会从数据流中恢复。
当遍历图形时,图形对象可能不支持Serializable接口。这时会出现NotSerializableException异常,并且会告知你没有实现Serializable接口的对象。
序列化过程和反序列化过程中,需要进行特殊处理的类,必须实现特定的方法:
private void writeObject(java.io.ObjectOutputStream out) throw IOException
private void readObject(java.io.ObjectInputStream in) throw IOException, ClassNotFoundException
private void readObjectNoData() throw ObjectStreamException
writeObject()方法负责写对象的特定的类,以便与之关联的readObject()方法可以恢复它。保存对象属性的默认机制可以调用out.defaultWriteObject()。这个方法不关心状态是属于父类或是子类。特定属性通过ObjectOutputStream的writeObject()方法使得对象的状态得以保存,基本数据类型通过DataOutput的方法使得对象的状态得以保存。
readObject()方法负责从数据流中读取并恢复类的属性。通过调用in.defaultReadObject方法恢复对象的非static、非transient的属性。defaultReadObject()方法将数据流中对象的属性和关联的当前对象的属性进行赋值。处理的场景是类新增了属性。这个方法不关心状态是属于父类或是子类。特定属性通过ObjectOutputStream的writeObject()方法使得对象的状态得以保存,基本数据类型通过DataOutput的方法使得对象的状态得以保存。
readObjectNoData()方法在反序列化时初始化那些不会列举出来的父类。可能发生在发送方和接收方发现实例的version不同时发生。也可能在数据流被篡改后发生。
在写入数据时,可变更的对象需要指定方法:
ANY-ACCESS_MODIFY Object writeReplace() throw ObjectSteamException
在序列化时该方法会被调用。因此该方法的权限可以是private, protected, package。子类访问这个方法遵循java的权限访问规则。
在流读取时,如果想替换类的实例,需要指定方法:
ANY-ACCESS-MODIFY Object readResolve() throw ObjectSteamException
readResolve()方法的调用规则和权限访问同writeReplace()一致。
序列化运行和类的version number相关--serialVersionUID。在序列化认证时有用。如果接收方发现serialVersionUID和发送方不同,则会抛出InvalidClassException。对象设置属性serialVersionUID的值,但必须是static, final, long。
例如:ANY-ACCESS-MODIFY static final long serialVersionUID = 42L。
如果一个Serialize类没有明确指定serialVersionUID的值,那么序列化过程时会自动计算出一个值并赋值。但强烈建议明确指定,因为该值的计算和编译器的实现有关,因此反序列化时有可能抛出InvalidClassException。并且同样强烈建议使用private修饰,因为该值不是用来继承的。数组的类可以不明确指定该值,所以数组的类放弃对比serialVersionUID的值。
Android N(targetSDKVersion)计算serialVersionUID有轻微的改变。为了保证兼容性,只有targetSDKVersion >= 24才会使用改变后的计算方式。强烈建议指定serialVersionUID,避免出现兼容性问题。
为了更好的使用Serialization,请参考《Effective Java》关于selializable的API。这本书阐释了如何使用该接口,以避免对你的应用程序的可维护性造成伤害。
建议性选择:
JSON是简洁的、可读性好、高效的。Android提供了JSONReader、和JSONObject进行json的读、写。使用GSON可以直接对JAVA对象进行读写,很方便。
/**
- @author unascribed
- @see java.io.ObjectOutputStream
- @see java.io.ObjectInputStream
- @see java.io.ObjectOutput
- @see java.io.ObjectInput
- @see java.io.Externalizable
- @since JDK1.1
*/
public interface Serializable {
}