java序列化

 

 

類要序列化,必須實現serializable接口(雖然這個接口是空的)

 

當對象被存儲時,該對象的類也必須同時被存儲,類的描述包括:

1)類的名稱。

2) 唯一的版本序列Id,這是數據域類型和方法簽名的指紋(SHA)

3) 一系列用來對序列化方法加以描述的標誌

4) 對數據域的描述

 

ObjectOutputStream.writeObject()不進保存了對象的全景圖,而且還能追蹤對象內包含的所有引用並保存這些對象。接着又對對象內包含的每個這樣的引用進行追蹤

如果不想某個屬性在序列化的時候被保存,可以使用transient進行修飾

不會保存static域

 

反序列化的時候,通過 ObjectInputStream.read()把這該對象讀出來。

賦值的時候,不通過構造函數來進行,而是直接賦值。

 

當有多個對象持有對同一個對象的引用時:

序列化的時候的原理

所有保存到磁盤的對象都有一個序列號

當向磁盤存儲一個對象時,先檢查相同的對象是否已經保存

如果已存儲,只需寫入”已經存儲的對象具有序列號x“。如果沒有,保存所有數據

 

讀取的過程是上述的逆過程。

 

 

也可以在實現serializable接口的對象中添加

private void writeObiect(ObjectOutputStream os) throws IOException

private void readObject(ObjectInputStream is) throws IOException

只要提供了這些方法,就會使用這兩個方法而不是默認的序列化機制,

可以在writeObject()內部調用os.defaultWriteObject()來執行默認的ObjectOutputStream.writeObject()操作。

readObject()也同理。

 

 

也可以通過實現Externalizable來做這些事情,需要重寫writeExtern()和readExtern()這兩個方法會在序列化的過程中被自動調用。

在默認情況下,externalizable不會保存人和字段,需要我們自己處理每個字段。

在恢復對象時候,先調用默認構造器,然後再調用readExternal()

 

 

 

版本:

 

如果類的定義發生了任何形式的變化,其SHA指紋也會改變。對象流會拒絕任何與指紋有異的對象。然而類也可以指出自己兼容於早期的版本。 一個類具有名爲serialVersionUID的靜態數據成員時,它將不再計算指紋,而是使用那個值。

一旦那個累的靜態數據成員被放入類中,通過序列化系統就可以讀取那個類的不同版本的對象。

如果只有類的方法變了,新對象數據的讀取不會有絲毫問題。

如果數據域發生了變化,對象流會與當前版本類中的數據域和當前的數據域做比較。當然,對象流考慮的僅是非臨時和非靜態的數據域。

如果兩個域名稱相同,但是類型不同,那麼流不會進行類型轉換。

如果流內含有的數據域在對象中不存在,那麼對象流會忽略該數據。

如果當前類擁有的數據域在流中不存在,那麼該數據域會被賦默認值(0,null,flase)

 

 

 

 

你所不知道的Java序列化

 

,Java序列化可以讓我們記錄下運行時的對象狀態(對象實例域的值),也就是我們經常說的對象持久化 。這個過程其實是非常複雜的,這裏我們就好好理解一下Java的對象序列化。

 

1、 首先我們要搞清楚,Java對象序列化是將 對象的實例域數據( 包括private私有域) 進行持久化存儲。而並非是將整個對象所屬的類信息進行存儲。 其實瞭解JVM的話,我們就能明白這一點了。實際上堆中所存儲的對象包含了實例域數據值以及指向類信息的地址,而對象所屬的類信息卻存放在方法區中。當我們要對持久層數據反序列化成對象的時候,也就只需要將實例域數據值存放在新創建的對象中即可。

 

2、 我們都知道凡要序列化的類都必須實現Serializable接口。 但是不是所有類都可以序列化呢?當然不是這樣,想想看序列化可以讓我們輕而易舉的接觸到對象的私有數據域,這是多麼危險的漏洞呀!總結一下,JDK中有四種類型的類對象是絕對不能序列化的 

     (1) 太依賴於底層實現的類(too closely tied to native code)。比如java.util.zip.Deflater。

     (2) 對象的狀態依賴於虛擬機內部和不停變化的運行時環境。比如java.lang.Thread, java.io.InputStream
     (3) 涉及到潛在的安全性問題。比如:java.lang.SecurityManager, java.security.MessageDigest
     (4) 全是靜態域的類,沒有對象實例數據。要知道靜態域本身也是存儲在方法區中的。

 

3、 自定義的類只要實現了Serializable接口,是不是都可以序列化呢?

 

 

包含了不可序列化的對象域的對象也是不能序列化的。 實際上,這也並非不可能,我們在下面第6點會談到。

 

4、 可序列化的類成功序列化之後,是不是一定可以反序列化呢? 包含了不可序列化的對象域的對象也是不能序列化的。 實際上,這也並非不可能,我們在下面第6點會談到。

 

5、 可序列化的類成功序列化之後,是不是一定可以反序列化呢? 

如果當前類的所有超類中有一個類即不能序列化,也沒有無參構造器。那麼當前類將不能反序列化。如果有無參構造器,那麼此超類反序列化的數據域將會是null或者0,false等等

 

 

 

6、但有時我們非常想將ZipFile所對應的本地文件路徑進行序列化,是不是真的沒有辦法了呢? 這裏我們就將一個非常有用的應用。

    當我們需要用writeObject(Object)方法對某個類對象序列化的時候,會首先對這個類對象的所有超類按照繼承層次從高到低來寫出每個超類的數據域。誰能保證每個超類都實現了Serializable接口呢? 其實,對於這些不能序列化的類,JVM會檢查這些類是否有這樣一個方法:

          private void writeObject(ObjectOutputStream out)throws IOException 
      如果有,JVM會調用這個方法仍然對該類的數據域進行序列化。

我們可以通過構造這個方法,使得原本不能序列化的類的部分數據域可以序列化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章