序列化
想要在intent上傳輸對象的時候,直接傳輸是做不到的,需要對這個對象處理一下,而這個處理的過程,就叫做序列化。
關於序列化的定義,百度百科這樣定義:
序列化 (Serialization)是將對象的狀態信息轉換爲可以存儲或傳輸的形式的過程。
比如說內存中有一組的對象集合,我們想要把這裏的數據持久的保存下來。
而序列化,就是將對象保存成一連串字節描述的過程;相反,反序列化,就是將這一連串的字節描述恢復成對象的過程。
我是學Android的嘛,序列化在Android中可以用在把對象在網絡上傳輸、intent意圖傳遞等。
目前準備學習的序列化有兩個,分別是Java的Serializable接口,還有Android特有的Parcelable接口,今天先着手一個較爲簡單的serializable接口。
Serializable:表示將一個對象轉化成可存儲或者可傳輸的狀態,序列化之後的對象可以在網絡上進行傳輸,也可以存儲在本地。
Parcelable:將一個完整的對象進行分解,而分解後的每一個部分都是Intent所能支持的數據類型,因此實現傳遞對象的功能。
關於Serializable和Parcelable的兩種方式,如何選擇?
關於兩者,先來看看它兩個之間的區別:
1. Serializable是通過使用IO流的形式將數據讀寫入在硬盤上;而Parcelable則是在內存中直接進行讀寫。
2. Serializable在序列化的過程中會產生大量的臨時文件,所以會產生頻繁GC;Parcelable是以IBinder爲載體,在內存中開銷較小。
3. 因爲Serializable是在磁盤中,所以它是數據持久化的;而Parcelable是在內存中,所以它不是。
從上面來看,Parcelable的效率要比Serializable高一些,一般情況下都是使用Parcelable,那麼什麼時候使用Serializable呢?
Parcelable不能使用在要將數據存儲在磁盤上的情況,因爲在外界有變化的情況下,Parcelable不能很好的保證數據的持續性。儘管Serializable效率低點,但此時還是建議使用Serializable 。
Serializable的用法
Serializable是Java所提供的一個序列化接口,它是一個空接口,爲對象提供標準的序列化和反序列化。
我們先將對象實現這個空接口,緊接這生成它所對應的 serialVersionUID 。
public class Girl implements Serializable {
private static final long serialVersionUID = -7684134237751913941L;
private String name;
private int birth;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getBirth() {
return birth;
}
public void setBirth(int birth) {
this.birth = birth;
}
}
serialVersionUID 一共有兩種實現,一種是默認的。
private static final long serialVersionUID = 1L;
另一種是根據包名、類名、參數、返回值等因素,生成一個64位哈希字段,是一個唯一值。
private static final long serialVersionUID = -7684134237751913941L;
先來看序列化:
private static void serializableGirl() throws IOException {
Girl girl = new Girl();
girl.setBirth(1997);
girl.setName("Seas");
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(
new File("/Users/suyichen/workspace/Project/EclipseWork/girl.txt")));
oos.writeObject(girl);
System.out.println("序列化 success");
}
對象的輸出流,將對象存入了指定的目錄中。其中的 oos.writeObject(girl) 將girl這個對象先進行序列化,然後把序列化得到的字節序列寫入到目標輸出流中。
看,已經在我的指定目錄下生成了文件。
再來看反序列化:
private static Girl deserializeGirl() throws Exception{
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(
new File("/Users/suyichen/workspace/Project/EclipseWork/girl.txt")));
Girl girl = (Girl) ois.readObject();
System.out.println("反序列化 success");
return girl;
}
對象輸入流,ois.readObject() 方法將指定輸入流中讀取字節序列,再將它轉化成爲對象。
注意:恢復後的對象和之前的對象雖然內容是完全一樣的,但兩者並不是同一個對象。
serialVersionUID
當序列化的時候,系統會把當前類的 serialVersionUID 寫入到序列化的文件中去;當反序列化的時候,系統再去檢測 serialVersionUID ,如果它與當前類的 serialVersionUID 一致,則證明之前序列化時類的版本和現在反序列化的版本相同,這時候可以成功的反序列化。
因爲上面說到,serialVersionUID 是根據根據包名、類名、參數、返回值等因素,生成一個64位哈希字段,是一個唯一值,一旦包名、類名、參數有變化,再次生成 serialVersionUID 的話,serialVersionUID 肯定是不同的。
假如我們指定了 serialVersionUID 的值,並且更改了一些成員變量,這時候,我們仍能反序列化成功,程序仍能最大限度恢復數據。
如果不去指定 serialVersionUID 的值,反序列時當前類有變化,那麼系統就會自動重新計算當前類的 serialVersionUID ,這時候當前類的 serialVersionUID 就與序列化的數據中 serialVersionUID 不同,然後反序列化失敗,程序crash。
不參與序列化的兩種情況
1. 靜態成員變量。因爲靜態成員變量是屬於類的,而並非屬於對象。
2. 用transient關鍵字標記的成員變量。
參考文獻
https://www.jianshu.com/p/af2f0a4b03b5