??? 序列化是將對(duì)象變?yōu)檫B續(xù)的字節(jié)流,用于對(duì)象的持久化,網(wǎng)絡(luò)傳輸?shù)葓?chǎng)合
一個(gè)類希望能被序列化必須實(shí)現(xiàn) Serializable 接口,Serializable 本身并沒有聲明任何
方法,只是起標(biāo)記作用。可序列化類的子類自然也是可序列化的。因而實(shí)現(xiàn)序列化是非常方
便的,只要在類的聲明時(shí)添加 implenment Serializable 就可以了,java 虛擬機(jī)會(huì)幫你處
理剩下的工作。序列化是遞歸的,一個(gè)類想要序列化則它的所有數(shù)據(jù)成員都必須是可以序列
化的,否則在序列化時(shí)會(huì)拋出 NotSerializableException 異常。jsdk 中基本數(shù)據(jù)類型的
封裝類(Integer, Float etc),Component 都是可序列化的,如果容器中的對(duì)象都是可
序列化的,則容器時(shí)可序列化的。java.lang.refect 包中的類不能序列化,Socket,
URLConnection 不能序列化。
對(duì)于只實(shí)現(xiàn)了 Serializable 接口的類,可以想象 java 是如何將它們序列化的:首先要利
用反射機(jī)制得到所有需要序列化的數(shù)據(jù)成員,包括 private 成員,得到 private 成員的過
程是非常規(guī)的,必定要經(jīng)過嚴(yán)格的權(quán)限和安全檢查。所以用 java 自己的序列化方式開銷是
非常大的,這也是為什么會(huì)有這篇文章的原因,這里要討論 java 提供的可以由我們自己控
制的序列化對(duì)象的方式。
覆寫
private void writeObject(ObjectOutputStream out)
private void readObject(ObjectInputStream in)
這兩個(gè)方法會(huì)在對(duì)象序列化和反序列化時(shí)被調(diào)用,通過覆寫這兩個(gè)方法我們可以完全控制整
個(gè)序列化的過程。我們注意到這兩個(gè)方法都是 private ,這意味我們無法顯示的調(diào)用這兩
個(gè)方法,而 java 虛擬機(jī)在調(diào)用這兩個(gè)方法時(shí)也必然要經(jīng)過嚴(yán)格的權(quán)限和安全檢查。與傳統(tǒng)
的序列化方式(只實(shí)現(xiàn) Serializable 接口)相比,這種方法減少了利用反射的次數(shù)以及獲
取 private 成員所需要的額外開銷,因?yàn)樵谶@兩個(gè)方法中所有的數(shù)據(jù)成員都是可以自由使
用的
實(shí)現(xiàn) Externalizable 接口
該接口中定義了兩個(gè)方法
public void readExternal(ObjectInput in) throws IOException
public void writeExternal(ObjectOutput out)
?????? throws IOException, ClassNotFoundException
這兩個(gè)方法會(huì)在對(duì)象序列化和反序列化時(shí)被調(diào)用。很明顯這兩個(gè)方法都是 public 的,所以
我們可以顯示的將一個(gè)對(duì)象序列化到一個(gè)輸出流,而 java 虛擬機(jī)在調(diào)用這兩個(gè)方法時(shí)也不
會(huì)有任何的限制。
ANY-ACCESS_MODIFIER Object writeReplace() throws ObjectStreamException;
ANY-ACCESS_MODIFIER Object readResolve() throws ObjectStreamException;
這兩個(gè)方法在序列化和反序列化時(shí)被調(diào)用,可以替換將要寫入或讀出的對(duì)象,在實(shí)現(xiàn)Singleton模式
時(shí)可能會(huì)用到
這三種方法實(shí)現(xiàn)序列化效率是顯而易見的:傳統(tǒng)方式最慢,實(shí)現(xiàn) Externalizable 接口方式
最快。但自己控制序列化過程有個(gè)明顯的缺點(diǎn)就是當(dāng)類的數(shù)據(jù)成員改變時(shí),序列化過程也同
時(shí)需要修改,相反這正好是傳統(tǒng)方式的優(yōu)點(diǎn):任何改動(dòng)都不會(huì)影響對(duì)象的正確序列化,虛擬
機(jī)會(huì)幫你完成一切工作,雖然不算出色。
當(dāng)我們考慮性能問題時(shí),序列化總應(yīng)該是我們首先要注意的方面,尤其是那些只實(shí)現(xiàn)了
Serializable 接口的類,它們往往就是性能的瓶頸所在,特別是一些對(duì)象需要反復(fù)的被序
列化和反序列化,實(shí)現(xiàn) Externalizable 接口會(huì)給你不小的驚喜。而對(duì)于實(shí)現(xiàn)
Externalizable 接口后需要保持?jǐn)?shù)據(jù)成員和序列化方法一致的問題實(shí)際算不上問題,因?yàn)?br />
當(dāng)我們考慮性能問題時(shí)應(yīng)該已經(jīng)到了編碼的最后階段,這時(shí)整體框架和數(shù)據(jù)結(jié)構(gòu)都已經(jīng)非常
穩(wěn)定了,數(shù)據(jù)成員被修改的可能已經(jīng)非常低了,即使被修改了,能大幅提高性能,多寫兩行代
碼也不是令人沮喪的事情。
以上只是單純的討論序列化的過程,實(shí)際上序列化總是和 I/O 操作同時(shí)發(fā)生,因?yàn)樾蛄谢?br />
就是為了傳輸或是存儲(chǔ),所以對(duì) I/O 的優(yōu)化方法在這里也是同樣適用的。
使用ObjectOutputStream.writeObject()時(shí),在流的內(nèi)部會(huì)有一個(gè)引用緩存,所有已經(jīng)寫入流的
對(duì)象如果再次被寫入則直接使用以前引用而不重新傳輸新的對(duì)象,這樣可以提到流的效率,但同樣帶來
問題,一個(gè)對(duì)象寫入流后,被修改,再次寫入流,再另一端ObjectInputStream得到的是
兩個(gè)相同的對(duì)象,這一點(diǎn)一定要注意
posted on 2005-09-09 14:07
JBahamut 閱讀(635)
評(píng)論(3) 編輯 收藏