1、概念
序列化是Java提供的用于保存對(duì)象狀態(tài)的機(jī)制,可以將內(nèi)存中各種對(duì)象的狀態(tài)(實(shí)例變量)保存下來,并且將保存的對(duì)象狀態(tài)再讀出來。
2、何時(shí)需要序列化
- 當(dāng)需要將內(nèi)存中對(duì)象的狀態(tài)保存到文件中或數(shù)據(jù)庫(kù)中時(shí),可以進(jìn)行序列化;
- 當(dāng)需要使用套接字在網(wǎng)絡(luò)上傳輸對(duì)象的時(shí)候,可以進(jìn)行序列化;
- 當(dāng)需要通過RMI傳輸對(duì)象的時(shí)候,可以進(jìn)行序列化;
3、序列化方式
在Java中通過socket傳輸數(shù)據(jù)時(shí),數(shù)據(jù)類型可以有很多種。常見的做法有兩種:一是把對(duì)象包裝成JSON字符串傳輸,二是采用java對(duì)象的序列化和反序列化。
4、如何實(shí)現(xiàn)序列化
需要實(shí)現(xiàn)序列化的類可以通過實(shí)現(xiàn) java.io.Serializable 接口以啟用其序列化功能,未實(shí)現(xiàn)此接口的類將無法使其任何狀態(tài)序列化或反序列化??尚蛄谢惖乃凶宇愋捅旧矶际强尚蛄谢?。序列化接口沒有方法或字段,僅用于標(biāo)識(shí)可序列化的語(yǔ)義。在反序列化過程中,將使用該類的公用或受保護(hù)的無參數(shù)構(gòu)造方法初始化不可序列化類的字段??尚蛄谢淖宇惐仨毮軌蛟L問無參數(shù)的構(gòu)造方法??尚蛄谢宇惖淖侄螌脑摿髦羞€原。
5、serialVersionUID
Java的序列化機(jī)制是通過在運(yùn)行時(shí)判斷類的serialVersionUID來驗(yàn)證版本一致性的。在進(jìn)行反序列化時(shí),JVM會(huì)把傳來的字節(jié)流中的serialVersionUID與本地相應(yīng)實(shí)體(類)的serialVersionUID進(jìn)行比較,如果相同就認(rèn)為是一致的,可以進(jìn)行反序列化,否則就會(huì)出現(xiàn)序列化版本不一致的異常。serialVersionUID 用來表明類的不同版本間的兼容性。有兩種生成方式:
- 一個(gè)是默認(rèn)的1L,比如:private static final long serialVersionUID = 1L;
- 一個(gè)是根據(jù)類名、接口名、成員方法及屬性等來生成一個(gè)64位的哈希字段,比如: private static final long serialVersionUID = xxxxL;
6、序列化與反序列化
當(dāng)兩個(gè)進(jìn)程在進(jìn)行遠(yuǎn)程通信時(shí),彼此可以發(fā)送各種類型的數(shù)據(jù)。無論是何種類型的數(shù)據(jù),都會(huì)以二進(jìn)制序列的形式在網(wǎng)絡(luò)上傳送。發(fā)送方需要把這個(gè)Java對(duì)象轉(zhuǎn)換為字節(jié)序列,才能在網(wǎng)絡(luò)上傳送;接收方則需要把字節(jié)序列再恢復(fù)為Java對(duì)象。
- 把Java對(duì)象轉(zhuǎn)換為字節(jié)序列的過程稱為對(duì)象的序列化。
- 把字節(jié)序列恢復(fù)為Java對(duì)象的過程稱為對(duì)象的反序列化。
對(duì)象的序列化主要有兩種用途:(1)把對(duì)象的字節(jié)序列永久地保存到硬盤上,通常存放在一個(gè)文件中; (2)在網(wǎng)絡(luò)上傳送對(duì)象的字節(jié)序列;
java.io.ObjectOutputStream代表對(duì)象輸出流,它的writeObject(Object obj)方法可對(duì)參數(shù)指定的obj對(duì)象進(jìn)行序列化,把得到的字節(jié)序列寫到一個(gè)目標(biāo)輸出流中。 java.io.ObjectInputStream代表對(duì)象輸入流,它的readObject()方法從一個(gè)源輸入流中讀取字節(jié)序列,再把它們反序列化為一個(gè)對(duì)象,并將其返回。
只有實(shí)現(xiàn)了Serializable和Externalizable接口的類的對(duì)象才能被序列化。Externalizable接口繼承自Serializable接口,實(shí)現(xiàn)Externalizable接口的類完全由自身來控制序列化的行為,而僅實(shí)現(xiàn)Serializable接口的類可以采用默認(rèn)的序列化方式 。
凡是實(shí)現(xiàn)Serializable接口的類都有一個(gè)表示序列化版本標(biāo)識(shí)符的靜態(tài)變量:private static final long serialVersionUID;
序列化運(yùn)行時(shí)使用一個(gè)稱為 serialVersionUID 的版本號(hào)與每個(gè)可序列化類相關(guān)聯(lián),該序列號(hào)在反序列化過程中用于驗(yàn)證序列化對(duì)象的發(fā)送者和接收者是否為該對(duì)象加載了與序列化兼容的類。如果接收者加載的該對(duì)象的類的 serialVersionUID 與對(duì)應(yīng)的發(fā)送者的類的版本號(hào)不同,則反序列化將會(huì)導(dǎo)致 InvalidClassException??尚蛄谢惪梢酝ㄟ^聲明名為serialVersionUID的字段(該字段必須是靜態(tài) (static)、最終 (final) 的 long 型字段)顯式聲明其自己的 serialVersionUID:
7、序列化機(jī)制
序列化分為兩大部分:序列化和反序列化。序列化是這個(gè)過程的第一部分,將數(shù)據(jù)分解成字節(jié)流,以便存儲(chǔ)在文件中或在網(wǎng)絡(luò)上傳輸。反序列化就是打開字節(jié)流并重構(gòu)對(duì)象。對(duì)象序列化不僅要將基本數(shù)據(jù)類型轉(zhuǎn)換成字節(jié)表示,有時(shí)還要恢復(fù)數(shù)據(jù)?;謴?fù)數(shù)據(jù)要求有恢復(fù)數(shù)據(jù)的對(duì)象實(shí)例。ObjectOutputStream中的序列化過程與字節(jié)流連接,包括對(duì)象類型和版本信 息。反序列化時(shí),JVM用頭信息生成對(duì)象實(shí)例,然后將對(duì)象字節(jié)流中的數(shù)據(jù)復(fù)制到對(duì)象數(shù)據(jù)成員中。ava.io包有兩個(gè)序列化對(duì)象的類。ObjectOutputStream負(fù)責(zé)將對(duì)象寫入字節(jié)流,ObjectInputStream從字節(jié)流重構(gòu)對(duì)象。序列化時(shí),類的所有數(shù)據(jù)成員應(yīng)可序列化除了聲明為transient或static的成員。將變量聲明為transient告訴JVM我們會(huì)負(fù)責(zé)將變?cè)蛄?化。將數(shù)據(jù)成員聲明為transient后,序列化過程就無法將其加進(jìn)對(duì)象字節(jié)流中,沒有從transient數(shù)據(jù)成員發(fā)送的數(shù)據(jù)。后面數(shù)據(jù)反序列化時(shí), 要重建數(shù)據(jù)成員(因?yàn)樗穷惗x的一部分),但不包含任何數(shù)據(jù),因?yàn)檫@個(gè)數(shù)據(jù)成員不向流中寫入任何數(shù)據(jù)。記住,對(duì)象流不序列化static或 transient。我們的類要用writeObject()與readObject()方法以處理這些數(shù)據(jù)成員。使用writeObject()與 readObject()方法時(shí),還要注意按寫入的順序讀取這些數(shù)據(jù)成員。
8、序列化的完全定制
如果一個(gè)類要完全負(fù)責(zé)自己的序列化,則實(shí)現(xiàn)Externalizable接口而不是Serializable接口。Externalizable接口定義包 括兩個(gè)方法writeExternal()與readExternal()。利用這些方法可以控制對(duì)象數(shù)據(jù)成員如何寫入字節(jié)流.類實(shí)現(xiàn) Externalizable時(shí),頭寫入對(duì)象流中,然后類完全負(fù)責(zé)序列化和恢復(fù)數(shù)據(jù)成員,除了頭以外,根本沒有自動(dòng)序列化。這里要注意了。聲明類實(shí)現(xiàn) Externalizable接口會(huì)有重大的安全風(fēng)險(xiǎn)。writeExternal()與readExternal()方法聲明為public,惡意類可 以用這些方法讀取和寫入對(duì)象數(shù)據(jù)。如果對(duì)象包含敏感信息,則要格外小心。這包括使用安全套接或加密整個(gè)字節(jié)流。
9、注意事項(xiàng)
- 序列化時(shí),只對(duì)對(duì)象的狀態(tài)進(jìn)行保存,而不管對(duì)象的方法;
- 當(dāng)一個(gè)父類實(shí)現(xiàn)序列化,子類自動(dòng)實(shí)現(xiàn)序列化,不需要顯式實(shí)現(xiàn)Serializable接口;
- 當(dāng)一個(gè)對(duì)象的實(shí)例變量引用其他對(duì)象,序列化該對(duì)象時(shí)也把引用對(duì)象進(jìn)行序列化;