Serializable和Parcelable接口可以完成对象的序列化过程,当我们需用使用Intent 和Binder 传输数据的时候,就需要使用到Parcel和Serializable.而有时候,我们需要持久化数据、或者是传输数据到网络上,我们也要使用Serializable来完成对象的序列化。
7.1Serializable接口
实现序列化接口,很简单,只需要实现它,就好,当然,最好是赋予它一个serialVersionUId (序列化ID)。不声明序列化ID,其实也可以序列化,但是,可能会对反序列化有影响。下面我们就简单介绍序列化和反序列化的过程:
假设我们有一个User类如下:
public class User impelements Serializable{
private static final long serialVersionUID = 519606712346586214L;
public int userId;
public String userName;
}
序列化过程需要用到ObjectOutputStream下面是序列化的过程:
User user = new User();
user.userId= 12;
user.userName = "张三";
ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream( "cache.txt") );
out.writeObject( user );
out.close();
通过上面的代码,对象就序列化到了cache.txt文件上了。
而这时候,我们希望能够把文件在变成对象,该怎么做呢?
就要进行反序列化过程,序列化的过程我们是使用ObjectOutputStream 那么反序列化当然是用ObjectInputStream
ObjectInputStream in = new ObjectOutputStream( new FileInputStream( "cache.txt"));
in.readObject();
in.close();
通过上面的代码,我们就能把对象从文件当中恢复,恢复出来的内容是完全一样的,但是他们不是同一个对象。
现在,我们在这边来讨论下serialVersionUID的作用吧,首先,就要从反序列化过程入手:
反序列化的时候,系统会查看文件的序列化ID,如果序列化ID一致,那么就会进行序列化,因为这代表对象是一样的,反之,则不会,因为序列化ID不一样说明了这个类已经被改变了,比如成员数量变化、类型变化,这时候就会反序列化失败。一般来说,我们要手动指定UID值,或者是用Eclipse等工具在创建的时候自动生成一个hash值,如果我们不指定会怎么样?不指定的话,系统序列化的时候,会把当前类的hash值作为序列化ID,那么当我们反序列化的时候,如果这个类改变了,那么hash值就会改变,那么就会造成序列化ID不一样,那么就导致了反序列化失败。
简单地说,不指定ID的话,我们存储User 时候有2个变量,而我们想获取原来User的数据的时候,我们改变了User类,改成了3个变量.增加了学号:number
这时候,从cache.txt想反序列化是不行的,因为cache的那个User是2个变量的User ,2个变量的User和3个变量的User的hash值改变了,所以系统认为两个不是一个版本的类,所以反序列化失败。
值得一提的是,序列化ID一样,并不一定会保证反序列化成功,比如我把userName从String改成了int,这样就会破坏了类的结构,或者是我改了类名也会造成这种情况。
7.2Parcelable接口
Parcelable也是一个接口,只要实现这个接口,这个类的对象就可以实现序列化,并且可以通过Intent和Biner进行传递。
下面是实现了Parcelable的例子:
public class User implements Parcelable {
public int userId;
public String userName;
public boolean isMale;
public Book book;
public User( int userId, String userName , boolean isMale )
{
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int arg1) {
out.writeInt( userId );
out.writeString(userName);
out.writeInt( isMale ? 1:0 );
out.writeParcelable(book, 0);
}
public Parcelable.Creator< User> CREATOR = new Parcelable.Creator<User>() {
@Override
public User createFromParcel(Parcel arg0) {
return new User(arg0) ;
}
@Override
public User[] newArray(int arg0) {
// TODO Auto-generated method stub
return new User[arg0];
}
};
private User( Parcel in )
{
userId = in.readInt();
userName = in.readString();
isMale = in.readInt() == 1 ? true : false;
book = in.readParcelable( Thread.currentThread().getContextClassLoader());
}
}
可以看到,反序列化的过程是由CREATOR来实现的。
系统已经为我们提供了很多实现了Parcelable的类,它们都是可以直接序列化的,比如Intent、Bundle、Bitmap等,
同时List和Map也可以序列化,前提是它们里面的每个元素都是可以序列化的。
Serializable是Java的接口,它使用起来比较简单,但是开销大,因为需要大量的I/O操作。而Parcel是Anroid多肚饿,他的效率比较高,适合用在内存交换当中,而如果是持久化数据或者是网路传输,采用Serializble比较好,因为Parcel实现比较复杂(也是可以持久化和传输)。