編輯:關於Android編程
Serializable接口是Java提供的一個序列化接口,它是一個空接口,為對象提供標准的序列化和反序列化操作。使用Serializable來實現的對象的序列化相當簡單,只需要在類的生命中指定一個類似相面的標識即可自動實現默認的序列化過程。
private static final long serialVersionUID=871238749032;
完整的對象序列化代碼示例如下:
//Model
public class User implements Serializable{
private static final long serialVersionUID=871238749032;
public int userId;
public String userName;
public String password;
}
//序列化到本地
User user=new User(0,"[email protected]","123456");
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("user.obj"));
out.writeObject(user);
out.close;
//反序列化
ObjectInputStream in=new ObjectInputStream(new FileInputStream("user.obj"));
User user=(User)in.readObject();
in.close();
這種方式是Java提供的一種序列化方式,過程非常簡單,甚至有些開發人員都不需要聲明serialVersionUID也可以完成這個過程,但serialVersionUID到底需不需要指定呢?
需要!
Java API既然提供了這個serialVersionUID,那麼它必定是有用的。這個serialVersionUID是用來輔助序列化和反序列化過程的,原則上序列化後的數據中的serialVersionUID只有和當前類的serialVersionUID相同才能夠正常地被反序列化。
serialVersionUID的詳細工作過程是這樣的:序列化的時候系統會把當前類的serialVersionUID寫入序列化的二進制文件中,當反序列化的時候系統會檢測文件中的serialVersionUID是否和當前類的serialVersionUID一致,如果一致就說明序列化的類的版本和當前類的版本是相同的,這個時候可以成功反序列化;否則說明當前類和反序列化的類相比發生了某些變化,比如成員變量的數量、類型發生了變化,這個時候是無法正常反序列化的。
一般來說,我們應該手動指定serialVersionUID的值,比如1L,也可以讓IDE根據當前類的結構自動去生成它的hash值,這樣序列化和反序列化時兩者的serialVersionUID是相同的,因此可以正常進行反序列化操作。如果不手動指定serialVersionUID的值
反序列化時當前類有些改變,比如增加或者刪除了某些成員變量,那麼系統就會重新計算當前類的hash值並把它賦值給serialVersionUID,這個時候當前類的serialVersionUID就和反序列化數據中的serialVersionUID不一致,就會造成反序列化失敗的結果。所以,手動指定serialVersionUID可以在很大程度上避免反序列化過程的失敗。
比如當版本升級後,我們可能刪除了某個成員變量也可能增加了一些新的成員變量,這個時候我們的反序列化過程依然能夠成功,程序仍然能夠最大限度地回復數據;相反,如果不指定serialVersionUID的話,程序會發生Crash。
當然,我們還需要考慮一種情況,如果類結構發生了非城規改變,比如修改了類名,修改了成員變量的類型,這個時候盡管serialVersionUID驗證通過了,但是反序列化過程仍然會失敗,因為類的結構有了毀滅性的改變,根本無法從老版本的數據中還原出一個新的類結構的對象。
對於使用序列化還有兩點需要注意:
1.靜態成員變量屬於類不屬於對象,所以不參與序列化過程
2.用transient關鍵字標記的成員變量不參與序列化過程
Parcelable接口是Android SDK提供的一種專門用於Android應用中對象的序列化和反序列化的方式,相比於Seriablizable具有更好的性能。實現Parcelable接口的對象就可以實現序列化並可以通過Intent和Binder傳遞。
下面是一個完成的實現了Parcelable接口的類
public class User implements Parcelable{
public int userId;
public String userName;
public String password;
public Book book;
public User(int userId,String userName,String password,Book book){
this.userId=userId;
this.userName=userName;
this.password=password;
this.book=book;
}
public int describeContents(){
//幾乎所有情況都返回0,僅在當前對象中存在文件描述符時返回1
return 0;
}
public void writeToParcel(Parcel out,int flags){
out.writeInt(userId);
out.writeString(userName);
out.writeString(password);
out.writeParcelable(book,0);
}
public static final Parcelable.Creator CREATOR=new Parcelable.Creator(){
public User createFromParcel(Parcel in){
return new User(in);
}
public User[] newArray(int size){
return new User[size];
}
}
private User(Parcel in){
userId=in.readInt();
userName=in.readString();
password=in.readString(); book=in.readParcelable(Thread.currentThread().getContextClassLoader());
}
}
看起來比Serializable方式復雜太多。我們使用表格把Parcelable方式的相關方法進行說明
既然Parcelable和Serializable都可以實現序列化並且可以用於Intent間的數據傳遞,那麼兩者有什麼區別呢?
前述:本人已工作兩年多,但是依然感覺還是Android的門外漢,之前一直從事Android的應用開發,每天就是各種調用SDK方法,各種拷貝網上的源碼以及jar包,從來也不
PS一句:最終還是選擇CSDN來整理發表這幾年的知識點,該文章平行遷移到CSDN。因為CSDN也支持MarkDown語法了,牛逼啊! 1 背景最近在簡書和微博還
前言:前面幾篇講了自定義控件繪制原理Android自定義控件基本原理詳解(一) ,Android自定義控件之自定義屬性(二) ,Android自定義控件之自定義組合控件(
歡迎大家關注Android開源網絡框架NoHttp:https://github.com/yanzhenjie/NoHttp 我們在實際開發中,很多App都