序列化 : Serializable 與 Parcelable ( 上 )

序列化

想要在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

https://blog.csdn.net/u013870094/article/details/82765907

https://www.jianshu.com/p/a8fc2616badc

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章