Android Serializable接口 详解

在编写程序,通常我们需要通过Intent和Binder传输数据时就需要使用Serialzable或者Pacelable完成对象的序列化过程。
Serializable是java所提供的一个序列化接口,其实是一个空接口,为对象提供标准的序列化和反序列化操作。在Android中也提供了新的序列化方式,那就是Parcelable接口,使用Parcelable来实现对象的序列号稍复杂点,本篇就先介绍Serializable接口。
而想使一个对象实现序列化只需让这个实现这个Serializable接口并声明一个serialVersionUID即可(这个serialVersionUID不是必需的,不用serialVersionUID也能实现序列化,但是这将会对反序列化过程产生影响,具体咱后面介绍)。
User类就是一个实现Serializable接口的类,它是可以被序列化和反序列化的,如下:

public class User implements Serializable{
private static final long serialVersionUID = 519067123721265773L;
public int userId;
public String userName;
public boolean isMale;
.......
}

通过seriaizable方式来实现对象的序列化,实现起来非常简单,几乎所有的工作都被系统自动完成了。如何进行对象的序列化和反序列化也非常简单,只需采用ObjectOutputStream和ObjectInputStream即可轻松实现,下面举个简单例子。

//  序列化过程
        User user = new User(0,"jake",true);
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
        out.writeObject(user);
        out.close();

    //反序列化
        ObjectInputStream  in = new ObjectInputStream(new FileInputStream("cache.txt"));
        User newUser = (User) in.readObject();
        in.close();

上述代码演示了采用了seaialzable方式序列化对象的典型过程,很简单,只需把实现了serializable接口的User对象写到文件中就可以快速恢复了,恢复后的对象newUser和user的内容是一样的,但是俩者并不是同一个对象。
刚开始提到,即使不指定serialVersonUID也可以实现序列化,到底要不要制定呢?如果指定的话,serialVersionUID后面那一长串数字又是什么含义呢?我们要明白,系统既然提供了这个serialVersionUID,那么他就必须是有用的,这个serialVersionUID是用来辅助序列化跟反序列化过程的,原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才能正常的反序列化,serialVersionUID的详细工作机制是这样的:序列化的时候系统会把当前类的serialVersionUID写入序列化的文件中(也可能是其他中介),当反序列化的时候系统会去检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变化,比如成员变量的数据,类型可能发生了改变,这个时候是无法反序列化的,因此会报如下错的:

java.io.InvaildClassException:Main;local class incompatible:stream classdesc serialVersionUID = 8711368828010083044,local class serialVersionUID =8711368828010083043.

一般来说,我们应该手动指定serialVersionUID的值,比如1L,也可以让EClipse或者AS根据当前类的结构自动去生成它的hash值,这样序列化跟反序列化时的俩者的serialVersionUID是相同的,因此可以正常进行反序列化。如果不手动指定serialVersionUID的值,反序列化时当前类有所改变,比如增加或者删除了某些成员变量,那么系统就会重新计算当前类的hash值并把它赋值给serialVersionUID,这个时候当前类的serialVersionUID就和序列化的数据中的serialVersionUID不一致,于是反序列化失败,程序就会出现crash。所以,我们可以明显感觉到serialVersionUID的作用,当我们手动指定了它以后,就可以很大程度上避免反序列化过程的失败。比如版本升级后,我们可能删除了某个成员变量也可能增加了一些新的成员变量,这个时候我们的反序列化过程仍然能够成功,程序仍然能最大限度的恢复数据,相反,如果不指定serialVersionUID的话,程序则会挂掉。当然我们还要考虑另外一种情况,如果类结构发生非常规性改变,比如修改了类名,修改了成员变量的类型,这个时候尽管serialVersionUID验证通过了,但是反序列化过程还是会失败,因为类结构有了毁灭性的改变,根本无法从老版本的数据中还原出一个新的类结构对象。
根据上面的分析,我们可以知道,给serialVersionUID指定1L或者采用eclipse(或AS)根据当前类结构去生成的hash值,这俩者并没有本质区别,效果完全一样。以下俩点需要特别提一下,首先静态成员变量属于类不属于对象,所以不会参与序列化过程;其次用transient关键词标记的成员变量不参与序列化过程。
另外,系统的默认序列化过程也是可以改变的,通过实现如下俩个方法即可重写系统默认的序列化跟反序列化过程,具体怎么去重写这俩个方法就是很简单的事了,这里就不再详细介绍了,并且大部分情况下我们不需要重写这两个方法。

  private void writeObject(java.io.ObjectOutputStream out)throws IOException{
    //write 'this' to 'out' ...

}
private void readObject(java.io.ObjectInputStream out)throws IOException,ClassNotFoundException{
    //populata the fields of 'this' from the data in 'in' ...
}

你可能感兴趣的:(android,笔记)