一. Serializable接口
该方法使用简单,只需要将类实现Serializable接口并声明一个serialVersionUID即可(serialVersionUID非必须,但建议)。
原因:为了避免因为增删一些成员变量,导致反序列化失败,建议手动指定serialVersionUID的值;如果类结构发生毁灭性改变,即便serialVersionUID验证通过,也会反序列化失败。
serialVersionUID的工作机制:序列化的时候系统会把当前类的serialVersionUID写入序列化的文件中(也可能是其他中介),当反序列化的时候系统就会去检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候就可以成功反序列化;否则就说明当前类和序列化的类相比发生了变化,比如成员变量的数量、类型发生了改变,这时是无法正常反序列化的,会有异常InvalidClassException。
注意:
- 静态成员变量属于类不属于对象,不会参与序列化过程;
- 用transient关键字标记的成员变量不参与序列化过程。
二. Parcelable接口
该方法使用起来麻烦一些,需要实现序列化、反序列化和内容描述等功能。
方法 | 功能 |
---|---|
createFromParcel(Parcel in) | 从序列化后的对象中创建原始对象 |
new Array(int size) | 创建指定长度的原始对象数组 |
User(Parcel in) | 从序列化后的对象中创建原始对象 |
writeToParcel(Parcel dest, int flags) | 将当前对象写入序列化结构中,其中flag表示有两种值:0或者1。为1时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况都为0 |
describeContents | 返回当前对象的内容描述。如果含有文件扫描符,返回1,否则返回0。几乎所有情况都为0 |
注意:
在以Parcel为参的构造函数中,如果有一个变量是可序列化对象,那么它的反序列化过程需要传递当前线程的上下文类加载器,否则会报无法找到类的错误。
book = in.readParcelable(Thread.currentThread().getContextClassLoader());
对比:
Serializable是Java中的序列化接口,使用简单但开销很大,序列化和反序列化过程都需要大量的I/O操作;Parcelable是Android中的序列化方式,更适用于Android平台,效率很高,但使用起来稍微麻烦点。内存序列化上,首选Parcelable;将对象序列化到存储设备或通过网络传输时,建议使用Serializable。