一、為什么要進(jìn)行序列化 再介紹之前,我們有必要先了解下對象的生命周期,我們知道Java中的對象都是存在于堆內(nèi)存中的,而堆內(nèi)存是可以被垃圾回收器不定期回收的。從對象被創(chuàng)建到被回收這一段時間就是Java對象的生命周期,也即Java對象只存活于這個時間段內(nèi)。 對象被垃圾回收器回收意味著對象和對象中的成員變量所占的內(nèi)存也就被回收,這意味著我們就再也得不到該對象的任何內(nèi)容了,因為已經(jīng)被銷毀了嘛,當(dāng)然我們可以再重新創(chuàng)建,但這時的對象的各種屬性都又被重新初始化了。所以如果我們需要保存某對象的狀態(tài),然后再在未來的某段時間將該對象再恢復(fù)出來的話,則必須要在對象被銷毀即被垃圾回收器回收之前保存對象的狀態(tài)。要保存對象狀態(tài)的話,我們可以使用文件、數(shù)據(jù)庫,也可以使用序列化,這里我們主要介紹對象序列化。我們很有必要了解這方面的內(nèi)容,因為對象序列化不僅在保存對象狀態(tài)時可以被用到(對象持久化),在Java中的遠(yuǎn)程方法調(diào)用RMI也會被用到,在網(wǎng)絡(luò)中要傳輸對象的話,則必須要對對象進(jìn)行序列化,關(guān)于RMI有機會我會再專門開貼介紹。 簡單總結(jié)起來,進(jìn)行對象序列化的話的主要原因就是實現(xiàn)對象持久化和進(jìn)行網(wǎng)絡(luò)傳輸,這里先只介紹怎樣通過對象序列化保存對象的狀態(tài)。 下面我們通過一個簡單的例子來介紹下如何進(jìn)行對象序列化。 二、怎樣進(jìn)行對象序列化 假設(shè)我們要保存Person類的某三個對象的name、age、height這三個成員變量,當(dāng)然這里只是簡單舉例 我們先看下Person類,要序列化某個類的對象的話,則該類必要實現(xiàn)Serializable接口,從Java API中我們發(fā)現(xiàn)該接口是個空接口,即該接口中沒聲明任何方法。 [java] view plaincopy
下面我們看一下如何來進(jìn)行序列化,這其中主要涉及到Java的I/O方面的內(nèi)容,主要用到兩個類FileOutputStream和ObjectOutputStream,FileOutputStream用于將字節(jié)輸出到文件,ObjectOutputStream通過調(diào)用writeObject方法將對象轉(zhuǎn)換為可以寫出到流的數(shù)據(jù)。所以整個流程是這樣的:ObjectOutputStream將要序列化的對象轉(zhuǎn)換為某種數(shù)據(jù),然后通過FileOutputStream連接某磁盤文件,再對象轉(zhuǎn)化的數(shù)據(jù)轉(zhuǎn)化為字節(jié)數(shù)據(jù)再將其寫出到磁盤文件。下面是具體代碼: [java] view plaincopy
三、對象的反序列化 我們存儲的目的主要是為了再恢復(fù)使用,下面我們來看下加上反序列化后的代碼: [java] view plaincopy
輸出結(jié)果如下: [java] view plaincopy
從添加的代碼我們可以看到進(jìn)行反序列化也很簡單,主要用到的流是FileInputstream和ObjectInputstream正好與存儲時用到的流相對應(yīng)。另外從結(jié)果順序我們可以看到反序列化后得到對象的順序與序列化時的順序一致。 四、總結(jié) 進(jìn)行對象序列化主要目的是為了保存對象的狀態(tài)(成員變量)。 進(jìn)行序列化主要用到的流是FileOutputStream和ObjectOutputStream。FileOutputStream主要用于連接磁盤文件,并把字節(jié)寫出到該磁盤文件;ObjectOutputStream主要用于將對象寫出為可轉(zhuǎn)化為字節(jié)的數(shù)據(jù)。 要將某類的對象序列化,則該類必須實現(xiàn)Serializable接口,該接口僅是一個標(biāo)志,告訴JVM該類的對象可以被序列化。如果某類未實現(xiàn)Serializable接口,則該類對象不能實現(xiàn)序列化。 保存狀態(tài)的目的就是為了在未來的某個時候再恢復(fù)保存的內(nèi)容,這可以通過反序列化來實現(xiàn)。對象的反序列化過程與序列化正好相反,主要用到的兩個流是FileInputstream和ObjectInputStream。 反序列化后得到的對象的順序與保存時的順序一致。 五、補充 補充一:上面我們舉得例子很簡單,要保存的成員變量要么是基本類型的要么是String類型的。但有時成員變量有可能是引用類型的,這是的情況會復(fù)雜一點。那就是當(dāng)要對某對象進(jìn)行序列化時,該對象中的引用變量所引用的對象也會被同時序列化,并且該對象中如果也有引用變量的話則該對象也將被序列化??偨Y(jié)說來就是在序列化的時候,對象中的所有引用變量所對應(yīng)的對象將會被同時序列化。這意味著,引用變量類型也都要實現(xiàn)Serializable接口。當(dāng)然其他對象的序列化都是自動進(jìn)行的。所以我們只要保證里面的引用類型是都實現(xiàn)Serializable接口就行了,如果沒有的話,會在編譯時拋出異常。如果序列化的對象中包含沒有實現(xiàn)Serializable的成員變量的話,這時可以使用transient關(guān)鍵字,讓序列化的時候跳過該成員變量。使用關(guān)鍵字transient可以讓你在序列化的時候自動跳過transient所修飾的成員變量,在反序列化時這些變量會恢復(fù)到默認(rèn)值。 補充二:如果某類實現(xiàn)了Serializable接口的話,其子類會自動編程可序列化的,這個好理解,繼承嘛。 補充三:在反序列化的時候,并不會調(diào)用對象的構(gòu)造器,這也好理解,如果調(diào)用了構(gòu)造器的話,對象的狀態(tài)不就又重新初始化了嗎。 補充四:我們說到對象序列化的是為了保存對象的狀態(tài),即對象的成員變量,所以靜態(tài)變量不會被序列化。 |
|