1.Serializable接口
序列化與反序列化就是JAVA對象與一串字節(jié)流之間的相互轉(zhuǎn)換, 我們在程序中創(chuàng)建的JAVA對象只存在于JVM中, 當(dāng)程序退出時, 這些對象也就消失了, 而序列化正是為了將這些對象保存起來以僅將來使用,也可以將已經(jīng)序列化的對象傳送給其他JVM來使用,這些序列化的字節(jié)流是于JVM無關(guān)的, 也就是說一個JVM序列化的對象可以在另一個JVM中反序列化.
序列化分為兩大部分:序列化和反序列化。 序列化是這個過程的第一部分酝掩,將數(shù)據(jù)分解成字節(jié)流鳞芙,以便存儲在文件中或在網(wǎng)絡(luò)上傳輸。 反序列化就是打開字節(jié)流并重構(gòu)對象。 對象序列化不僅要將基本數(shù)據(jù)類型轉(zhuǎn)換成字節(jié)表示原朝,有時還要恢復(fù)數(shù)據(jù)驯嘱。 恢復(fù)數(shù)據(jù)要求有恢復(fù)數(shù)據(jù)的對象實(shí)例。 ObjectOutputStream中的序列化過程與字節(jié)流連接喳坠,包括對象類型和版本信息鞠评。 反序列化時,JVM用頭信息生成對象實(shí)例丙笋,然后將對象字節(jié)流中的數(shù)據(jù)復(fù)制到對象數(shù)據(jù)成員中谢澈。
Java的對象序列化只要對象實(shí)現(xiàn)了Serializable接口
這個接口只是一個標(biāo)記接口煌贴,不包含任何的方法御板,
使用JAVA提供的序列化機(jī)制有以下兩條需要遵守的條件:
1.該類必須直接實(shí)現(xiàn)java.io.Serializable接口或者間接從其繼承樹中實(shí)現(xiàn)該接口(也就是他的某個父類實(shí)現(xiàn)了這個接口);
2.對于該類的所有無法序列化的屬性(本文指字段field, 而不是嚴(yán)格意義上的屬性property, 下同)必須使用transient修飾.
補(bǔ)充說明:
從其繼承樹中實(shí)現(xiàn)Serializable接口指的是該類的某個父類實(shí)現(xiàn)了這個接口, 要注意的是Object類并沒有實(shí)現(xiàn)該接口, 也就是說默認(rèn)的情況下我們定義的類是不支持序列化的, 而JDK提供的某些類如String, 數(shù)組等實(shí)現(xiàn)了該接口;
無法序列化的屬性包括兩種:一種是主觀上不想保存的屬性, 如動態(tài)生成的屬性或者考慮到性能上的要求不準(zhǔn)備保存的屬性; 另一種是由于該屬性的類型沒有實(shí)現(xiàn)序列化而無法保存的屬性, 如Thread類型的屬性;
安全方面的原因,比如一個對象擁有private牛郑,public等field怠肋,對于一個要傳輸?shù)膶ο螅热鐚懙轿募团螅蛘哌M(jìn)行rmi傳輸?shù)鹊润细鳎谛蛄谢M(jìn)行傳輸?shù)倪^程中,這個對象的private等域是不受保護(hù)的础芍。
transient
變量修飾符杈抢,如果用transient聲明一個實(shí)例變量,當(dāng)對象存儲時仑性,它的值不需要維持惶楼。
換句話來說就是,用transient關(guān)鍵字標(biāo)記的成員變量不參與序列化過程诊杆。Serializable序列化不保存靜態(tài)變量歼捐,可以使用Transient關(guān)鍵字對部分字段不進(jìn)行序列化,也可以覆蓋writeObject晨汹、readObject方法以實(shí)現(xiàn)序列化過程自定義豹储。
場景:某個類的有些屬性需要序列化,而其他屬性不需要被序列化淘这,打個比方剥扣,如果一個用戶有一些敏感信息(如密碼,銀行卡號等)铝穷,為了安全起見钠怯,不希望在網(wǎng)絡(luò)操作(主要涉及到序列化操作,本地序列化緩存也適用)中被傳輸氧骤,這些信息對應(yīng)的變量就可以加上transient關(guān)鍵字呻疹。換句話說,這個字段的生命周期僅存于調(diào)用者的內(nèi)存中而不會寫到磁盤里持久化。
序列化過程
如果我們想要序列化一個對象刽锤,首先要創(chuàng)建某些OutputStream(如FileOutputStream镊尺、ByteArrayOutputStream等),然后將這些OutputStream封裝在一個ObjectOutputStream中并思。這時候庐氮,只需要調(diào)用writeObject()方法就可以將對象序列化,并將其發(fā)送給OutputStream(記姿伪恕:對象的序列化是基于字節(jié)的弄砍,不能使用Reader和Writer等基于字符的層次結(jié)構(gòu))。而飯序列的過程(即將一個序列還原成為一個對象)输涕,需要將一個InputStream(如FileInputstream音婶、ByteArrayInputStream等)封裝在ObjectInputStream內(nèi),然后調(diào)用readObject()即可莱坎。
序列化算法一般會按步驟做如下事情:
◆將對象實(shí)例相關(guān)的類元數(shù)據(jù)輸出衣式。
◆遞歸地輸出類的超類描述直到不再有超類。
◆類元數(shù)據(jù)完了以后檐什,開始從最頂層的超類開始輸出對象實(shí)例的實(shí)際數(shù)據(jù)值碴卧。
◆從上至下遞歸輸出實(shí)例的數(shù)據(jù)
對象序列化過程不僅僅保存單個對象,還能追蹤對象內(nèi)所包含的所有引用乃正,并保存那些對象(這些對象也需實(shí)現(xiàn)了Serializable接口)
任何類型只要實(shí)現(xiàn)了Serializable接口住册,就可以被保存到文件中,或者作為數(shù)據(jù)流通過網(wǎng)絡(luò)發(fā)送到別的地方瓮具。
反序列化
序列化機(jī)制并不要求該類具有一個無參的構(gòu)造方法, 因?yàn)樵诜葱蛄谢倪^程中實(shí)際上是去其繼承樹上找到一個沒有實(shí)現(xiàn)Serializable接口的父類(最終會找到Object), 然后構(gòu)造該類的對象, 再逐層往下的去設(shè)置各個可以反序列化的屬性(也就是沒有被transient修飾的非靜態(tài)屬性).
在反序列的JVM上必須能夠找到該類(有可能序列化和反序列化并不是在同一個JVM上進(jìn)行的), 否則就會拋出ClassNotFoundException;
由于ObjectInputStream.readObject()方法可以反序列化任何類的對象, 所以其返回類型為Object, 我們需要將其強(qiáng)轉(zhuǎn)成具體的類;
如何對不滿足序列化機(jī)制的兩個要求的類進(jìn)行序列化, 則會拋出NotSerializableException;
如果JVM發(fā)現(xiàn)序列化與反序列化的類文件"不相同", 則會拋出InvalidClassException.
Parcelable 實(shí)現(xiàn)過程
1.實(shí)現(xiàn)Parcelable接口
2.實(shí)現(xiàn)接口中的兩個方法
public int describeContents();
public void writeToParcel(Parcel dest, int flags);
第一個方法是內(nèi)容接口描述荧飞,默認(rèn)返回0就可以了
第二個方法是將我們的對象序列化一個Parcel對象,也就是將我們的對象存入Parcel中 .
3.實(shí)例化靜態(tài)內(nèi)部對象CREATOR實(shí)現(xiàn)接口Parcelable.Creator搭综,實(shí)例化CREATOR時要實(shí)現(xiàn)其中的兩個方法垢箕,其中createFromParcel的功能就是從Parcel中讀取我們的對象。
對比分析
Serializable是Java的實(shí)現(xiàn)方式,可能會頻繁的IO操作,所以消耗比較大,但是實(shí)現(xiàn)方式簡單 Parcelable是Android提供的方式,效率比較高,但是實(shí)現(xiàn)起來復(fù)雜一些Parcelable的性能比Serializable好兑巾,在內(nèi)存開銷方面較小条获,所以在內(nèi)存間數(shù)據(jù)傳輸時推薦使用Parcelable,如activity間傳輸數(shù)據(jù)蒋歌,而Serializable可將數(shù)據(jù)持久化方便保存帅掘,所以在需要保存或網(wǎng)絡(luò)傳輸數(shù)據(jù)時選擇Serializable.
在讀寫數(shù)據(jù)的時候,Parcelable是在內(nèi)存中直接進(jìn)行讀寫,而Serializable是通過使用IO流的形式將數(shù)據(jù)讀寫入在硬盤上.
Serializable在序列化操作的時候會產(chǎn)生大量的臨時變量,(原因是使用了反射機(jī)制)從而可能導(dǎo)致GC的頻繁調(diào)用。
Parcelable是以Ibinder作為信息載體的.在內(nèi)存上的開銷比較小,因此在內(nèi)存之間進(jìn)行數(shù)據(jù)傳遞的時候,使用Parcelable,