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實現比較複雜(也是可以持久化和傳輸)。