Effective Java(3rd)-Item86 非常謹慎地實現(xiàn)Serializable

??允許對類的實例進行序列化可以非常簡單,只需將單詞implements Serializable添加到其聲明中即可勋陪。因為這很容易做到皮服,所以有一個普遍的誤解,認為序列化只需要程序員付出很少的努力逢防。事實要復雜得多。雖然使類可序列化的即時成本可以忽略不計蒲讯,但長期成本通常是巨大的忘朝。
??實現(xiàn)Serializable的一個主要成本是,一旦類的實現(xiàn)被釋放判帮,它就會降低更改該類實現(xiàn)的靈活性局嘁。當類實現(xiàn)Serializable時,其字節(jié)流編碼(或序列化形式)成為其導出API的一部分晦墙。一旦廣泛分發(fā)了一個類悦昵,通常就需要永遠支持序列化的表單,就像需要支持導出API的所有其他部分一樣晌畅。如果您不努力設計自定義序列化表單但指,而只是接受默認值,則序列化表單將永遠綁定到類的原始內(nèi)部表示抗楔。換句話說棋凳,如果您接受默認的序列化表單,類的私有和包私有實例字段將成為其導出API的一部分谓谦,并且最小化字段訪問的實踐(item15)將失去其作為信息隱藏工具的有效性贫橙。

??如果您接受默認的序列化表單,然后更改類的內(nèi)部表示反粥,則會導致序列化表單中不兼容的更改。試圖使用類的舊版本序列化實例并使用新版本反序列化實例的客戶機(反之亦然)將經(jīng)歷程序失敗。在維護原始序列化表單的同時才顿,可以更改內(nèi)部表示(使用ObjectOutputStream.putFields和ObjectInputStream.readFields),但是這很困難莫湘,并且會在源代碼中留下明顯的缺陷。如果您選擇使類可序列化郑气,您應該仔細設計一個高質(zhì)量的序列化表單幅垮,以便長期使用(第87、90項)尾组。這樣做會增加開發(fā)的初始成本忙芒,但是這樣做是值得的。即使是設計良好的序列化形式讳侨,也會限制類的演化;設計不良的序列化表單可能會造成嚴重后果呵萨。
??可串行化性對演化施加的約束的一個簡單示例涉及流惟一標識符(通常稱為串行版本)
uid。每個可序列化的類都有一個與之關(guān)聯(lián)的惟一標識號跨跨。如果您沒有通過聲明一個名為serialVersionUID的靜態(tài)final長字段來指定這個數(shù)字潮峦,系統(tǒng)在運行時通過對類的結(jié)構(gòu)應用加密哈希函數(shù)(SHA-1)自動生成它。這個值受類的名稱勇婴、它實現(xiàn)的接口及其大多數(shù)成員(包括編譯器生成的合成成員)的影響忱嘹。如果你改變了這些的任何,比如耕渴,通過添加一個方便的方法拘悦,生成的串行版本UID發(fā)生了變化。如果您未能聲明串行版本UID橱脸,兼容性將被破壞窄做,從而在運行時導致InvalidClassException異常。
??實現(xiàn)Serializable的第二個代價是增加了bug和安全漏洞的可能性(item85 )慰技。通常椭盏,對象是用構(gòu)造函數(shù)創(chuàng)建的;序列化是一種用于創(chuàng)建對象的超語言機制。無論您接受默認行為還是覆蓋它吻商,反序列化都是一個“隱藏構(gòu)造函數(shù)”掏颊,與其他構(gòu)造函數(shù)具有相同的所有問題。由于沒有與反序列化關(guān)聯(lián)的顯式構(gòu)造函數(shù)艾帐,因此很容易忘記必須確保它保證構(gòu)造函數(shù)建立的所有不變量乌叶,并且不允許攻擊者訪問正在構(gòu)造的對象的內(nèi)部。賴默認的反序列化機制可以很容易地讓對象暴露于不變的破壞和非法訪問(item88 )柒爸。

??實現(xiàn)Serializable的第三個成本是准浴,它增加了與發(fā)布類的新版本相關(guān)的測試負擔。當一個可序列化的類被修改時捎稚,重要的是檢查是否可以在新版本中序列化一個實例乐横,并在舊版本中反序列化它求橄,反之亦然。因此葡公,所需的測試量與可序列化類的數(shù)量和版本的數(shù)量成正比罐农,這兩個數(shù)量可能很大。您必須確保序列化-反序列化過程成功催什,并確保它生成原始對象的忠實副本涵亏。如果在第一次編寫類時仔細設計了自定義序列化表單,那么測試的需求就會減少 (Items 87, 90).
??實現(xiàn)Serializable并不是一個輕松的決定蒲凶。如果一個類要參與一個依賴于Java序列化來進行對象傳輸或持久性的框架气筋,這是非常重要的。此外旋圆,它還極大地簡化了將類作為必須實現(xiàn)Serializable的另一個類中的組件的使用宠默。然而,與實現(xiàn)Serializable相關(guān)的成本很多臂聋。每次設計一個類時光稼,都要權(quán)衡利弊。歷史上孩等,像BigInteger和Instant這樣的值類實現(xiàn)了序列化艾君,集合類也實現(xiàn)了序列化。表示活動實體(如線程池)的類很少應該實現(xiàn)Serializable肄方。
??為繼承而設計的類(item19)應該很少實現(xiàn) 可序列化的冰垄,接口很少應該擴展它。違反此規(guī)則會給擴展類或?qū)崿F(xiàn)接口的任何人帶來很大的負擔权她。有時違反規(guī)則是適當?shù)暮绮琛@纾绻粋€類或接口的存在主要是為了參與一個要求所有參與者實現(xiàn)Serializable的框架隅要,那么類或接口實現(xiàn)或擴展Serializable可能是有意義的蝴罪。

??為繼承而設計的實現(xiàn)序列化的類包括
Throwable和Component。Throwable實現(xiàn)了Serializable步清,因此RMI可以將異常從服務器發(fā)送到客戶機要门。Component實現(xiàn)了序列化
gui可以被發(fā)送、保存和恢復廓啊,但是即使在Swing和AWT的鼎盛時期欢搜,這個工具在實踐中也很少使用。
??如果您使用實例字段實現(xiàn)了一個既可序列化又可擴展的類谴轮,那么需要注意幾個風險炒瘟。如果實例字段值上有任何不變量,關(guān)鍵是要防止子類覆蓋finalize方法第步,該類可以通過覆蓋finalize并聲明它為final來實現(xiàn)這一點疮装。否則缘琅,該類將容易受到終結(jié)器攻擊(item8)。最后斩个,如果類的實例字段初始化為默認值(整數(shù)類型為0胯杭,布爾值為false驯杜,對象引用類型為null)受啥,那么必須添加readObjectNoData方法:

image.png

??這個方法是在Java 4中添加的,以涵蓋一個涉及到將可序列化超類添加到現(xiàn)有可序列化類[serializable, 3.5]的特殊情況鸽心。
??關(guān)于不實現(xiàn)Serializable的決定滚局,有一個警告。如果為繼承而設計的類不可序列化顽频,則可能需要額外的工作來編寫可序列化的子類藤肢。此類的常規(guī)反序列化要求超類具有可訪問的無參數(shù)構(gòu)造函數(shù)[Serialization, 1.10]。如果不提供這樣的構(gòu)造函數(shù)糯景,子類將被迫使用序列化代理模式( item90 )嘁圈。

??內(nèi)部類(內(nèi)部類(第24項)不應該實現(xiàn)Serializable。)不應該實現(xiàn)Serializable蟀淮。它們使用編譯器生成的合成字段存儲對封閉實例的引用最住,并存儲來自封閉范圍的局部變量的值。這些字段與類定義的對應關(guān)系怠惶,以及匿名類和本地類的名稱都是未指定的涨缚。因此,內(nèi)部類的默認序列化形式定義不正確策治。但是脓魏,靜態(tài)成員類可以實現(xiàn)Serializable。
??總而言之通惫,實現(xiàn)Serializable的簡單性是似是而非的茂翔。除非類只在受保護的環(huán)境中使用,在這種環(huán)境中履腋,版本永遠不必互操作珊燎,服務器永遠不會暴露給不可信的數(shù)據(jù),否則實現(xiàn)
Serializable是一個嚴肅的承諾府树,應該非常謹慎地做出俐末。如果類允許繼承,則需要格外小心奄侠。
本文寫于2019.7.24卓箫,歷時1天

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市垄潮,隨后出現(xiàn)的幾起案子烹卒,更是在濱河造成了極大的恐慌闷盔,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旅急,死亡現(xiàn)場離奇詭異逢勾,居然都是意外死亡,警方通過查閱死者的電腦和手機藐吮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門溺拱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谣辞,你說我怎么就攤上這事迫摔。” “怎么了泥从?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵句占,是天一觀的道長。 經(jīng)常有香客問我躯嫉,道長纱烘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任祈餐,我火速辦了婚禮擂啥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘昼弟。我一直安慰自己啤它,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布舱痘。 她就那樣靜靜地躺著变骡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪芭逝。 梳的紋絲不亂的頭發(fā)上塌碌,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音旬盯,去河邊找鬼台妆。 笑死,一個胖子當著我的面吹牛胖翰,可吹牛的內(nèi)容都是我干的接剩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼萨咳,長吁一口氣:“原來是場噩夢啊……” “哼懊缺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起培他,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤鹃两,失蹤者是張志新(化名)和其女友劉穎遗座,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體俊扳,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡途蒋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了馋记。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片号坡。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖抗果,靈堂內(nèi)的尸體忽然破棺而出筋帖,到底是詐尸還是另有隱情奸晴,我是刑警寧澤冤馏,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站寄啼,受9級特大地震影響逮光,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜墩划,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一涕刚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乙帮,春花似錦杜漠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至氢卡,卻和暖如春锈至,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背译秦。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工峡捡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人筑悴。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓们拙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親阁吝。 傳聞我的和親對象是個殘疾皇子砚婆,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容