紅色主題網(wǎng)站模板seo網(wǎng)站內(nèi)部?jī)?yōu)化
要想使一個(gè)類(lèi)的實(shí)例可被序列化,非常簡(jiǎn)單,只要在它的聲明中加入"implements Serializable"字樣即可。雖然使一個(gè)類(lèi)可被序列化的直接開(kāi)銷(xiāo)低到甚至可以忽略不計(jì),但是為了序列化而付出的長(zhǎng)期開(kāi)銷(xiāo)往往是實(shí)實(shí)在在的。
為實(shí)現(xiàn)Serializable而付出的最大代價(jià)是,一旦一個(gè)類(lèi)被發(fā)布,就大大降低了"改變這個(gè)類(lèi)的實(shí)現(xiàn)"的靈活性。 如果一個(gè)類(lèi)實(shí)現(xiàn)了Serializable,它的字節(jié)流編碼(或者說(shuō)序列化形式,serialized form)就變成了它的導(dǎo)出的API的一部分。一旦這個(gè)類(lèi)被廣泛使用,往往必須永遠(yuǎn)支持這種序列化形式,就好像你必須要支持導(dǎo)出的API的所有其他部分一樣。如果你不努力設(shè)計(jì)一個(gè)自定義的序列化形式(custom serialized form),而僅僅接受了默認(rèn)的序列化形式,這種序列化形式將被永遠(yuǎn)地束縛在該類(lèi)最初的內(nèi)部表示法上。換句話說(shuō),如果你接受了默認(rèn)的序列化形式,這個(gè)類(lèi)中私有的和包級(jí)私有的實(shí)例域?qū)⒍甲兂蓪?dǎo)出的API的一部分,這不符合"最低限度地訪問(wèn)域"的實(shí)踐準(zhǔn)則(見(jiàn)第13條),從而它就失去了作為信息隱藏工具的有效性。
如果你接受了默認(rèn)的序列化形式,并且以后又要改變這個(gè)類(lèi)的內(nèi)部表示法,結(jié)果可能導(dǎo)致序列化形式的不兼容??蛻?hù)端程序企圖用這個(gè)類(lèi)的舊版本來(lái)序列化一個(gè)類(lèi),然后用新版本進(jìn)行反序列化,結(jié)果將導(dǎo)致程序失敗。在改變內(nèi)部表示法的同時(shí)仍然維持原來(lái)的序列化形式(使用ObjectOutputStream.putFields和ObjectInputStream.readFields),這也是可能的,但是做起來(lái)比較困難,并且會(huì)在源代碼中留下一些可以明顯的隱患。因此,你應(yīng)該仔細(xì)地設(shè)計(jì)一種高質(zhì)量的序列化形式,并且在很長(zhǎng)時(shí)間內(nèi)都愿意使用這種形式(見(jiàn)第75,78條)。這樣做將會(huì)增加開(kāi)發(fā)的初始成本,但這是值得的。設(shè)計(jì)良好的序列化形式也許會(huì)給類(lèi)的演變帶來(lái)限制;但是設(shè)計(jì)不好的序列化形式則可能會(huì)使類(lèi)根本無(wú)法演變。
序列化會(huì)使類(lèi)的演變受到限制,這種限制的一個(gè)例子與流的唯一標(biāo)識(shí)符(stream unique identifier)有關(guān),通常它也被稱(chēng)為序列版本UID(serial version UID)。每個(gè)可序列化的類(lèi)都有一個(gè)唯一標(biāo)識(shí)號(hào)與它相關(guān)聯(lián)。如果你沒(méi)有在一個(gè)名為serialVersionUID的私有靜態(tài)final的long域中顯式地指定該標(biāo)識(shí)號(hào),系統(tǒng)就會(huì)自動(dòng)地將一個(gè)復(fù)雜的過(guò)程作用在這個(gè)類(lèi)上,從而在運(yùn)行時(shí)產(chǎn)生該標(biāo)識(shí)號(hào)。這個(gè)自動(dòng)產(chǎn)生的值會(huì)受到類(lèi)名稱(chēng)、它所實(shí)現(xiàn)的接口的名稱(chēng)、以及所有公有的和受保護(hù)的成員的名稱(chēng)所影響。如果你通過(guò)任何方式改變了這些信息,比如,增加了一個(gè)不是很重要的工具方法,自動(dòng)產(chǎn)生的序列版本UID也會(huì)發(fā)生變化。因此,如果你沒(méi)有聲明一個(gè)顯式的序列版本UID,兼容性將會(huì)遭到破壞,在運(yùn)行時(shí)導(dǎo)致InvalidClassException異常。
實(shí)現(xiàn)Serializable的第二個(gè)代價(jià)是,它增加了出現(xiàn)Bug和安全漏洞的可能性。 通常情況下,對(duì)象是利用構(gòu)造器來(lái)創(chuàng)建的;序列化機(jī)制是一種語(yǔ)言之外的對(duì)象創(chuàng)建機(jī)制(extralinguistic mechanism)。無(wú)論你是接受了默認(rèn)的行為,還是覆蓋了默認(rèn)的行為,反序列化機(jī)制(deserialization)都是一個(gè)"隱藏的構(gòu)造器",具備與其他構(gòu)造器相同的特點(diǎn)。因?yàn)榉葱蛄谢瘷C(jī)制中沒(méi)有顯式的構(gòu)造器,所以你很容易忘記要確保:反序列化過(guò)程必須也要保證所有"由構(gòu)造器建立起來(lái)的約束關(guān)系",并且不允許攻擊者訪問(wèn)正在構(gòu)造過(guò)程中的對(duì)象的內(nèi)部信息。依靠默認(rèn)的反序列化機(jī)制,可以很容易地使對(duì)象的約束關(guān)系遭到破壞,以及遭受到非法訪問(wèn)(見(jiàn)第76條)。
實(shí)現(xiàn)Serializable的第三個(gè)代價(jià)是,隨著類(lèi)發(fā)行新的版本,相關(guān)的測(cè)試負(fù)擔(dān)也增加了。 當(dāng)一個(gè)可序列化的類(lèi)被修訂的時(shí)候,很重要的一點(diǎn)是,要檢查是否可以"在新版本中序列化一個(gè)實(shí)例,然后在舊版本中反序列化",反之亦然。因此,測(cè)試所需要的工作量與"可序列化的類(lèi)的數(shù)量和發(fā)行版本號(hào)"的乘積成正比,這個(gè)乘積可能會(huì)非常大。這些測(cè)試不可能自動(dòng)構(gòu)建,因?yàn)槌硕M(jìn)制兼容性(binary compatibility)以外,你還必須測(cè)試語(yǔ)義兼容性(semantic compatibility)。換句話說(shuō),你必須既要確保"序列化-反序列化"過(guò)程成功,也要確保結(jié)果產(chǎn)生的對(duì)象真正是原始對(duì)象的復(fù)制品??尚蛄谢?lèi)的變化越大,它就越需要測(cè)試。如果在最初編寫(xiě)一個(gè)類(lèi)的時(shí)候,就精心設(shè)計(jì)了自定義的序列化形式,測(cè)試的要求就可以有所降低,但是也不能完全沒(méi)有測(cè)試。
實(shí)現(xiàn)Serializable接口并不是一個(gè)很輕松就可以做出的決定。它提供了一些實(shí)在的益處:如果一個(gè)類(lèi)將要加入到某個(gè)框架中,并且該框架依賴(lài)于序列化來(lái)實(shí)現(xiàn)對(duì)象傳輸或者持久化,對(duì)于這個(gè)類(lèi)來(lái)說(shuō),實(shí)現(xiàn)Serializable接口就非常有必要。更進(jìn)一步來(lái)看,如果這個(gè)類(lèi)要成為另一個(gè)類(lèi)的一個(gè)組件,并且后者必須實(shí)現(xiàn)Serializable接口,若前者也實(shí)現(xiàn)了Serializable接口,它就會(huì)更易于被后者使用。 然而,有許多實(shí)際的開(kāi)銷(xiāo)都與實(shí)現(xiàn)Serializable接口有關(guān)。每當(dāng)你實(shí)現(xiàn)一個(gè)類(lèi)的時(shí)候,都需要權(quán)衡一下所付出的代價(jià)和帶來(lái)的好處。根據(jù)經(jīng)驗(yàn),比如Date和BigInteger這樣的值類(lèi)應(yīng)該實(shí)現(xiàn)Serializable,大多數(shù)的集合類(lèi)也應(yīng)該如此。代表活動(dòng)實(shí)體的類(lèi),比如線程池(thread pool),一般不應(yīng)該實(shí)現(xiàn)Serializable。
為了繼承而設(shè)計(jì)的類(lèi)(見(jiàn)第17條)應(yīng)該很少實(shí)現(xiàn)Serializable,接口也應(yīng)該很少會(huì)擴(kuò)展它。如果違反了這條規(guī)則,擴(kuò)展這個(gè)類(lèi)或者實(shí)現(xiàn)這個(gè)接口的程序員就會(huì)背上沉重的負(fù)擔(dān)。然而在有些情況下違反這條規(guī)則卻是合適的。例如,如果一個(gè)類(lèi)或者接口存在的目的主要是為了參與到某個(gè)框架中,該框架要求所有的參與者都必須實(shí)現(xiàn)Serializable,
那么,對(duì)于這個(gè)類(lèi)或者接口來(lái)說(shuō),實(shí)現(xiàn)或者擴(kuò)展Serializable就是非常有意義的。
為了繼承而設(shè)計(jì)的類(lèi)中真正實(shí)現(xiàn)了Serializable的有Throwable、Component和HttpServlet。因?yàn)門(mén)hrowable實(shí)現(xiàn)了Serializable,所以RMI的異??梢詮姆?wù)器端傳到客戶(hù)端。Component實(shí)現(xiàn)了Serializable,因此GUI可以被發(fā)送、保存和恢復(fù)。HttpServlet實(shí)現(xiàn)了Serializable,因此會(huì)話狀態(tài)可以被緩存。
如果你實(shí)現(xiàn)了一個(gè)帶有實(shí)例域的類(lèi),它是可序列化和可擴(kuò)展的,你就應(yīng)該擔(dān)心這樣一條告誡。
如果一個(gè)專(zhuān)門(mén)為了繼承而設(shè)計(jì)的類(lèi)不是可序列化的,就不可能編寫(xiě)出可序列化的子類(lèi)。特別是,如果超類(lèi)沒(méi)有提供可訪問(wèn)的無(wú)參構(gòu)造器,子類(lèi)也不可能做到可序列化。因此,對(duì)于為繼承而設(shè)計(jì)的不可序列化的類(lèi),你應(yīng)該考慮提供一個(gè)無(wú)參構(gòu)造器。
內(nèi)部類(lèi)不應(yīng)該實(shí)現(xiàn)Serializable。它們使用編譯器產(chǎn)生的合成域(synthetic field)來(lái)保存指向外圍實(shí)例(enclosing instance)的引用,以及保存來(lái)自外圍作用域的局部變量的值。"這些域如何對(duì)應(yīng)到類(lèi)定義中"并沒(méi)有明確的規(guī)定,就好像沒(méi)有指定匿名類(lèi)和局部類(lèi)的名稱(chēng)一樣。因此,內(nèi)部類(lèi)的默認(rèn)序列化形式是定義不清楚的。然而,靜態(tài)成員類(lèi)(static member class)卻可以實(shí)現(xiàn)Serializable。
簡(jiǎn)而言之,千萬(wàn)不要認(rèn)為實(shí)現(xiàn)Serializable會(huì)很容易。除非一個(gè)類(lèi)在用了一段時(shí)間之后就會(huì)被拋棄,否則,實(shí)現(xiàn)Serializable就是個(gè)很?chē)?yán)肅的承諾,必須認(rèn)真對(duì)待。如果一個(gè)類(lèi)是為了繼承而設(shè)計(jì)的,則更加需要加倍小心。對(duì)于這樣的類(lèi)而言,在"允許子類(lèi)實(shí)現(xiàn)Serializable"或"禁止子類(lèi)實(shí)現(xiàn)Serializable"兩者之間的一個(gè)折衷設(shè)計(jì)方案是,提供一個(gè)可訪問(wèn)的無(wú)參構(gòu)造器。這種設(shè)計(jì)方案允許(但不要求)子類(lèi)實(shí)現(xiàn)Serializable。
所有文章無(wú)條件開(kāi)放,順手點(diǎn)個(gè)贊不為過(guò)吧!
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??