1.Serializable 接口
Serializable是Java所提供的一个序列化的接口,它是一个空接口,为对象提供标准的序列化和反序列化操作。使用serializable来实现序列化很简单,一个类实现Serializable接口即可。
public class Person implements Serializable { //不写的话,IDE会生成默认的。 private static final long serialVersionUID = 314651345L; }
关于实现Serializable接口的建议是显示的去声明UID。为什么?
这个serialVersionUID是用来辅助序列化和反序列化过程的,原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才能够正常地被反序列化。它的详细的工作机制是这样的:序列化的时候系统会把当前类的serialVersionUID写入序列化的文件中,当反序列化的时候系统会把当前类的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变换,比如成员变量的数量、类型可能发生了改变,这个时候是无法正常反序列化的。会报java.io.InvalidClassException的错误。
所以,我们应该手动指定serialVersionUID的值,当改动类的改动很小时,这样可以“欺骗”JVM说我的类没有改动,这样序列化和反序列化时两者的serialVersionUID是相同的,因此可以正常进行序列化。
如果不手动指定的话,反序列化当前类有所改变时,比如增加或者删除了某些成员变量,那么系统就会根据包名,类名,继承关系、非私有的方法和属性,以及参数,返回值等各种重新计算出来的唯一值,这时候的当前类的serialVersionUID就和序列化的数据中的serialVersionUID不一致,所以反序列化失败,程序就会出现crash。
当然我们还要考虑另外一种情况,如果类结构发生了非常规性改变,比如修改了类名,这个时候尽管serialVersionUID验证通过了,但是反序列化还是会失败,因为类结构有了毁灭性的改变,根本无法从老版本的数据中还原出一个新的类结构的对象。
在改善Java程序建议11有个小DEMO可以稍微看下。
2.Parcelable 接口
Parcelable相对比较复杂,但是有一套模板代码可以参考,所有的实现Parcelable接口的类都跟模板代码类似。
public class CrimeParclable implements Parcelable {
private String mTitle;
private Boolean mSolved = false;
private String mSuspect;
public Person person;
public CrimeParclable(String title, Boolean solved, String suspect) {
this.mTitle = title;
this.mSolved = solved;
this.mSuspect = suspect;
}
/私有构造器,供Creator调用
private CrimeParclable(Parcel in) {
mTitle = in.readString();
mSolved = in.readInt() == 1;
mSuspect = in.readString();
person = in.readParcleable(Thread.currentThread().getContextClassLoader());
}
public static final Creator<CrimeParclable> CREATOR = new Creator<CrimeParclable>() {
@Override
public CrimeParclable createFromParcel(Parcel in) {
return new CrimeParclable(in);
}
@Override
public CrimeParclable[] newArray(int size) {
return new CrimeParclable[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mTitle);
dest.writeInt(mSolved ? 1 : 0);
dest.writeString(mSuspect);
dest.writeParcelable(person, 0);
}
}
序列化功能由writeToParcel来完成,最终是通过Parcel中的一系列write方法来完成的;反序列化功能由CREATOR来完成,其内部表明了如何创建序列化对象和数据,并通过Parcel的一系列read方法来完成反序列化的过程;内容描述功能由describeContents方法来完成,几乎在所有情况下这个方法都是返回0,仅当当前对象中存在文件描述符时,此方法返回1.需要注意的是,由于person是另一个可序列化对象,所以它的反序列化过程需要传递当前线程的上下文类加载器,否则会报无法找到类的错误。
3.选择哪一种方式序列化?
Serializable是Java中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量I\O操作。而Parcelable是Android中序列化方式,因此更适合用在Android平台上,它的缺点是使用起来稍微复杂一点,但是因为有一套模板,所以这点复杂还是可以接受的,而且它的效率很高,这也是Android推荐的序列化方式,因此在Android平台上首选Parcelable。