What叭首?
何為序列化與反序列化?
序列化:將對(duì)象轉(zhuǎn)化為二進(jìn)制序列的過(guò)程
反序列化:將二進(jìn)制序列恢復(fù)為原始對(duì)象的過(guò)程
Why踪栋?
為什么需要序列化焙格?
由于在系統(tǒng)底層,數(shù)據(jù)以簡(jiǎn)單的字節(jié)序列形式進(jìn)行傳遞夷都,即在底層眷唉,系統(tǒng)不認(rèn)識(shí)對(duì)象,只認(rèn)識(shí)字節(jié)序列囤官,所以為了達(dá)到跨進(jìn)程通訊的目的冬阳,需要先對(duì)數(shù)據(jù)進(jìn)行序列化;其次党饮,在進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)傳輸或者activity間對(duì)象傳遞時(shí)肝陪,也需要先將對(duì)象轉(zhuǎn)化為字節(jié)序列。
How刑顺?
如何進(jìn)行序列化氯窍?
在Android中饲常,序列化操作有兩種方式:實(shí)現(xiàn)Serializabale接口或?qū)崿F(xiàn)Parcelable接口。
Serializabale接口
Serializabale接口是一個(gè)空接口狼讨,實(shí)際上只提供標(biāo)記的功能贝淤,標(biāo)記實(shí)現(xiàn)了該接口的對(duì)象是可以進(jìn)行序列化的,而具體的序列化與反序列化操作是由ObjectOutputStream和ObjectInputStream完成的政供。序列化與反序列化過(guò)程均對(duì)用戶(hù)透明播聪,其中需要保存許多額外的字段以保證反序列化過(guò)程能夠順利完成,同時(shí)布隔,在這個(gè)過(guò)程中犬耻,還涉及到Java反射機(jī)制,所以整體時(shí)空開(kāi)銷(xiāo)比較大执泰。
Parcelable接口
Parcelable是Android提供的接口枕磁,它主要是通過(guò)writeToParcel(),將需要持久化的字段保存到一個(gè)Parcel對(duì)象里面术吝,然后通過(guò)CREATOR從Parcel對(duì)象中计济,取出相應(yīng)的字段,完成對(duì)象的恢復(fù)過(guò)程排苍。這整個(gè)過(guò)程均由用戶(hù)自己控制沦寂,可以自定義保存和恢復(fù)的字段,所以存儲(chǔ)代價(jià)小很多淘衙。
Serializabale VS Parcelable
實(shí)際開(kāi)發(fā)中传藏,推薦使用Parcelable接口,理由大致有如下三點(diǎn):首先彤守,Parcelable接口是Android提供使用的毯侦,Google提供了比較好的文檔和技術(shù)支持;其次具垫,Parcelable接口底層是內(nèi)存的copy侈离,而Serializable底層是文件IO操作,同時(shí)會(huì)使用到反射技術(shù)筝蚕,所以效率上卦碾,Parcelable要遠(yuǎn)高于Serializable;最后起宽,Parcelable具有更好地可控性洲胖,我們可以自己控制需要保存和恢復(fù)的字段,同時(shí)節(jié)省空間開(kāi)銷(xiāo)坯沪。Parcleable主要用于內(nèi)存序列化绿映,通過(guò)Parcelable將對(duì)象序列化到存儲(chǔ)設(shè)備中或者將對(duì)象序列化后通過(guò)網(wǎng)絡(luò)進(jìn)行傳輸也是可以的,但是這個(gè)過(guò)程會(huì)稍顯復(fù)雜屏箍,因此在這兩種情況下绘梦,建議使用Serializable橘忱。
序列化相關(guān)知識(shí)
序列化 ID
當(dāng)通過(guò)實(shí)現(xiàn)Serializable接口實(shí)現(xiàn)類(lèi)的序列化操作時(shí),需要提供一個(gè)序列化ID卸奉,即聲明private static final long serialVersionUID钝诚,原則上序列化后的數(shù)據(jù)中的serialVersionUID只有和當(dāng)前類(lèi)的serialVersionUID相同時(shí),才能夠進(jìn)行正常的反序列化操作榄棵,否則反序列化過(guò)程會(huì)失敗凝颇。serialVersionUID的詳細(xì)工作機(jī)制為:序列化的時(shí)候,系統(tǒng)會(huì)將當(dāng)前類(lèi)的serialVersionUID寫(xiě)入序列化文件/指定文件中疹鳄,當(dāng)反序列化的時(shí)候拧略,系統(tǒng)會(huì)檢測(cè)文件中的serialVersionUID與當(dāng)前類(lèi)中的serialVersionUID是否一致,若一致瘪弓,說(shuō)明序列化的類(lèi)的版本與當(dāng)前類(lèi)的版本相同垫蛆,這個(gè)時(shí)候可以成功進(jìn)行反序列化操作;否則腺怯,說(shuō)明當(dāng)前類(lèi)和序列化的類(lèi)相比袱饭,發(fā)生了某些變化,如成員變量的數(shù)量呛占、類(lèi)型等發(fā)生變化虑乖,這時(shí)反序列化操作就無(wú)法正常完成。靜態(tài)變量序列化
靜態(tài)變量是類(lèi)變量晾虑,不屬于某個(gè)對(duì)象疹味,而序列化操作保存的是對(duì)象的狀態(tài),即對(duì)象的成員變量帜篇,所以靜態(tài)變量不會(huì)參與序列化過(guò)程糙捺。父類(lèi)的序列化
如果希望一個(gè)類(lèi)的父類(lèi)也被序列化,則該父類(lèi)也應(yīng)實(shí)現(xiàn)序列化接口坠狡。Transient 關(guān)鍵字
Transient用于聲明一個(gè)變量不參與序列化過(guò)程继找,所以當(dāng)希望某些變量不被序列化時(shí),就可以使用該關(guān)鍵字修飾這些變量逃沿。被Transient關(guān)鍵字聲明的變量,在反序列化時(shí)幻锁,會(huì)被設(shè)置為初始值凯亮,即int類(lèi)型變量為0,對(duì)象類(lèi)型變量為null哄尔。對(duì)敏感字段加密
在消息傳輸?shù)倪^(guò)程中假消,有些字段是敏感字段,不希望被泄露岭接,如用戶(hù)密碼等富拗,在這種情況下臼予,進(jìn)行序列化操作時(shí),應(yīng)先對(duì)敏感字段進(jìn)行加密操作啃沪,反序列化時(shí)再進(jìn)行解密粘拾。具體解決辦法為:重寫(xiě)類(lèi)中的writeObject和readObject方法。因?yàn)樵谛蛄谢^(guò)程中创千,虛擬機(jī)會(huì)先調(diào)用待序列化類(lèi)中的writeObject和readObject方法缰雇,若該方法存在,則使用該方法完成用戶(hù)自定義的序列化和反序列化操作追驴,否則械哟,調(diào)用 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法進(jìn)行默認(rèn)的序列化和反序列化操作。序列化存儲(chǔ)規(guī)則
Java 序列化機(jī)制為了節(jié)省磁盤(pán)空間殿雪,具有特定的存儲(chǔ)規(guī)則暇咆,當(dāng)對(duì)同一對(duì)象進(jìn)行多次序列化操作時(shí),并不會(huì)對(duì)該對(duì)象的內(nèi)容進(jìn)行多次存儲(chǔ)丙曙,而只存儲(chǔ)多份引用糯崎,這樣就只需要保存新增的引用及一些控制信息。
ArrayList的序列化
由上圖一可知河泳,ArrayList實(shí)現(xiàn)了Serializable接口沃呢,可以進(jìn)行序列化操作,而由圖二可知拆挥,其中保存內(nèi)容的數(shù)組array被transient關(guān)鍵字修飾薄霜,不會(huì)被序列化,那么問(wèn)題來(lái)了纸兔,這到底是咋回事呢惰瓜?
原因如下圖三所示:
ArrayList內(nèi)部實(shí)現(xiàn)了writeObject和readObject方法,通過(guò)這兩個(gè)方法自己控制序列化過(guò)程汉矿。
Why transient崎坊?
ArrayList為什么使用transient使array不被序列化,然后又自定義序列化過(guò)程呢洲拇?
因?yàn)锳rrayList是一個(gè)動(dòng)態(tài)數(shù)組奈揍,經(jīng)常會(huì)成倍自增長(zhǎng)長(zhǎng)度,當(dāng)數(shù)組中實(shí)際存放的元素很少赋续,而申請(qǐng)的長(zhǎng)度比較大時(shí)男翰,直接進(jìn)行序列化,就會(huì)生成很多null元素纽乱,所以為了避免不必要的null元素的生成蛾绎,及提高時(shí)空效率,ArrayList將array用transient關(guān)鍵字進(jìn)行聲明,然后再自己控制元素的序列化過(guò)程租冠。
總結(jié)
- 在進(jìn)行跨進(jìn)程通信鹏倘、activity間數(shù)據(jù)傳輸以及網(wǎng)絡(luò)數(shù)據(jù)傳輸時(shí),需要將原始的對(duì)象類(lèi)型轉(zhuǎn)化為字節(jié)序列顽爹,這是就需要使用到對(duì)象的序列化與反序列化操作纤泵;
- 序列化即將對(duì)象或數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)化為字節(jié)序列的過(guò)程;反序列化即將字節(jié)序列恢復(fù)為原始對(duì)象的過(guò)程话原;序列化保存的是對(duì)象的“狀態(tài)”夕吻,即對(duì)象的成員變量,所以靜態(tài)變量繁仁、靜態(tài)方法等屬于類(lèi)的屬性以及被transient關(guān)鍵字聲明的不用于序列化的屬性涉馅,均不會(huì)被序列化;
- Android中序列化操作有兩種方式:實(shí)現(xiàn)Serializable接口和實(shí)現(xiàn)Parcleable接口黄虱。Serialiable是Java提供的序列化接口,使用起來(lái)比較簡(jiǎn)單稚矿,但是開(kāi)銷(xiāo)很大,序列化和反序列化均需要大量I/O操作捻浦,而Parcelable是Android提供的序列化方式晤揣,因此更適合用于Android平臺(tái),所以在Android平臺(tái)上朱灿,首選Parcelable接口昧识。
- ArrayList的序列化過(guò)程比較特殊,需要注意盗扒。