1.Serializable接口
序列化與反序列化就是JAVA對象與一串字節流之間的相互轉換, 我們在程序中創建的JAVA對象只存在于JVM中, 當程序退出時, 這些對象也就消失了, 而序列化正是為了將這些對象保存起來以僅將來使用,也可以將已經序列化的對象傳送給其他JVM來使用,這些序列化的字節流是于JVM無關的, 也就是說一個JVM序列化的對象可以在另一個JVM中反序列化.
序列化分為兩大部分:序列化和反序列化。 序列化是這個過程的第一部分,將數據分解成字節流,以便存儲在文件中或在網絡上傳輸。 反序列化就是打開字節流并重構對象。 對象序列化不僅要將基本數據類型轉換成字節表示,有時還要恢復數據。 恢復數據要求有恢復數據的對象實例。 ObjectOutputStream中的序列化過程與字節流連接,包括對象類型和版本信息。 反序列化時,JVM用頭信息生成對象實例,然后將對象字節流中的數據復制到對象數據成員中。
Java的對象序列化只要對象實現了Serializable接口
這個接口只是一個標記接口,不包含任何的方法,
使用JAVA提供的序列化機制有以下兩條需要遵守的條件:
1.該類必須直接實現java.io.Serializable接口或者間接從其繼承樹中實現該接口(也就是他的某個父類實現了這個接口);
2.對于該類的所有無法序列化的屬性(本文指字段field, 而不是嚴格意義上的屬性property, 下同)必須使用transient修飾.
補充說明:
從其繼承樹中實現Serializable接口指的是該類的某個父類實現了這個接口, 要注意的是Object類并沒有實現該接口, 也就是說默認的情況下我們定義的類是不支持序列化的, 而JDK提供的某些類如String, 數組等實現了該接口;
無法序列化的屬性包括兩種:一種是主觀上不想保存的屬性, 如動態生成的屬性或者考慮到性能上的要求不準備保存的屬性; 另一種是由于該屬性的類型沒有實現序列化而無法保存的屬性, 如Thread類型的屬性;
安全方面的原因,比如一個對象擁有private,public等field,對于一個要傳輸的對象,比如寫到文件,或者進行rmi傳輸等等,在序列化進行傳輸的過程中,這個對象的private等域是不受保護的。
transient
變量修飾符,如果用transient聲明一個實例變量,當對象存儲時,它的值不需要維持。
換句話來說就是,用transient關鍵字標記的成員變量不參與序列化過程。Serializable序列化不保存靜態變量,可以使用Transient關鍵字對部分字段不進行序列化,也可以覆蓋writeObject、readObject方法以實現序列化過程自定義。
場景:某個類的有些屬性需要序列化,而其他屬性不需要被序列化,打個比方,如果一個用戶有一些敏感信息(如密碼,銀行卡號等),為了安全起見,不希望在網絡操作(主要涉及到序列化操作,本地序列化緩存也適用)中被傳輸,這些信息對應的變量就可以加上transient關鍵字。換句話說,這個字段的生命周期僅存于調用者的內存中而不會寫到磁盤里持久化。
序列化過程
如果我們想要序列化一個對象,首先要創建某些OutputStream(如FileOutputStream、ByteArrayOutputStream等),然后將這些OutputStream封裝在一個ObjectOutputStream中。這時候,只需要調用writeObject()方法就可以將對象序列化,并將其發送給OutputStream(記住:對象的序列化是基于字節的,不能使用Reader和Writer等基于字符的層次結構)。而飯序列的過程(即將一個序列還原成為一個對象),需要將一個InputStream(如FileInputstream、ByteArrayInputStream等)封裝在ObjectInputStream內,然后調用readObject()即可。
序列化算法一般會按步驟做如下事情:
◆將對象實例相關的類元數據輸出。
◆遞歸地輸出類的超類描述直到不再有超類。
◆類元數據完了以后,開始從最頂層的超類開始輸出對象實例的實際數據值。
◆從上至下遞歸輸出實例的數據
對象序列化過程不僅僅保存單個對象,還能追蹤對象內所包含的所有引用,并保存那些對象(這些對象也需實現了Serializable接口)
任何類型只要實現了Serializable接口,就可以被保存到文件中,或者作為數據流通過網絡發送到別的地方。
反序列化
序列化機制并不要求該類具有一個無參的構造方法, 因為在反序列化的過程中實際上是去其繼承樹上找到一個沒有實現Serializable接口的父類(最終會找到Object), 然后構造該類的對象, 再逐層往下的去設置各個可以反序列化的屬性(也就是沒有被transient修飾的非靜態屬性).
在反序列的JVM上必須能夠找到該類(有可能序列化和反序列化并不是在同一個JVM上進行的), 否則就會拋出ClassNotFoundException;
由于ObjectInputStream.readObject()方法可以反序列化任何類的對象, 所以其返回類型為Object, 我們需要將其強轉成具體的類;
如何對不滿足序列化機制的兩個要求的類進行序列化, 則會拋出NotSerializableException;
如果JVM發現序列化與反序列化的類文件"不相同", 則會拋出InvalidClassException.
Parcelable 實現過程
1.實現Parcelable接口
2.實現接口中的兩個方法
public int describeContents();
public void writeToParcel(Parcel dest, int flags);
第一個方法是內容接口描述,默認返回0就可以了
第二個方法是將我們的對象序列化一個Parcel對象,也就是將我們的對象存入Parcel中 .
3.實例化靜態內部對象CREATOR實現接口Parcelable.Creator,實例化CREATOR時要實現其中的兩個方法,其中createFromParcel的功能就是從Parcel中讀取我們的對象。
對比分析
Serializable是Java的實現方式,可能會頻繁的IO操作,所以消耗比較大,但是實現方式簡單 Parcelable是Android提供的方式,效率比較高,但是實現起來復雜一些Parcelable的性能比Serializable好,在內存開銷方面較小,所以在內存間數據傳輸時推薦使用Parcelable,如activity間傳輸數據,而Serializable可將數據持久化方便保存,所以在需要保存或網絡傳輸數據時選擇Serializable.
在讀寫數據的時候,Parcelable是在內存中直接進行讀寫,而Serializable是通過使用IO流的形式將數據讀寫入在硬盤上.
Serializable在序列化操作的時候會產生大量的臨時變量,(原因是使用了反射機制)從而可能導致GC的頻繁調用。
Parcelable是以Ibinder作為信息載體的.在內存上的開銷比較小,因此在內存之間進行數據傳遞的時候,使用Parcelable,