謹慎的使用 Serializable 接口(74)

  • 序列化帶來的直接開銷很是低,可是長期開銷是實實在在的

實現 Serializable 接口最大的代價就是java

  • 一旦一個類發佈,大大下降了改變其實現的靈活性
  • 由於此時,它的字節流編碼就變成了其導出API的一部分
  • 若是不設計一種自定義序列化形式,僅僅使用默認序列化,
    • 那麼私有和包級私有實例,都變成導出api的一部分
    • 不符合最低限度訪問域的準則
    • 默認序列化可能出現新老版本序列化和反序列化不兼容
      • 仍然保留老接口,會帶來別的問題、隱患
    • 仔細設計一種高質量的序列化形式,長期使用,初始付出的成本是值得的
  • 序列化會使得類的演變受到限制
    • 流的惟一標識符有關(序列版本UID)
      • private static final long serialVersionUID = 1L;
      • 若是不定義該標識符,會在運行時,調用一個複雜的過程自動生成
        • 並且內部改變,會致使,自動計算id 值改變,序列化兼容被打破
          • 致使InvalidClassException

實現 Serializable 第二個代價:api

  • 增長了出現 BUG、安全漏洞的可能性
    • 對象是構造器建立的
    • 序列化機制是語言以外的對象建立機制
      • 反序列化機制都是一個隱藏的構造器
        • 該構造器相對於真正的構造器,約束條件每每被忽略
      • 默認序列化機制的反序列化過程的約束關係很容易遭到破壞、非法訪問

第三個代價:緩存

  • 隨着類發行新的版本、相關測試負擔也增長了
    • 一個可序列化的類被修訂後,要檢查,在新版本序列化一個類,在老版本是否能夠反序列化,反之亦然
    • 測試工做量乘積增加

實現Serializable 確實帶來了益處:安全

  • 好比一些值類:Date、BigInteger 能夠實現Serializable
  • 活動實體類:Thread pool 通常不實現Serializable

爲了繼承而設計的類、接口,儘量少的實現 Serializable 接口框架

  • 可是若是專門設計參與到某個框架的類,該框架要求必須實現Serializable 時例外
  • 爲了繼承而設計的類,實現了Serializable 接口的有
    • Throwable類:RMI異常,能夠從服務端傳到客戶端
    • Component :GUI 能夠被髮送保存和恢復
    •  HttpServlet抽象類:會話狀態能夠被緩存
  • 若是實現帶有實例域的類,實例域被初始化成默認值會違背約束條件
    • 就必須添加下文中的方法

若是一個專門爲了繼承而設計的類不是可序列化的,測試

  • 就不可能編寫出可序列化的子類。
  • 特別是,若是超類沒有提供可訪問的無參構造器,子類也不可能作到可序列化。
  • 所以,對於爲繼承而設計的不可序列化的類,你應該考慮提供一個無參構造器。

內部類不該該實現Serializable。編碼

  • 它們使用編譯器產生的合成域來保存指向外圍實例的引用,
  • 以及保存來自外圍做用域的局部變量的值。
  • 所以,內部類的默認序列化形式是定義不清楚的。
  • 然而,靜態成員類倒是能夠實現Serializable接口。

千萬不要認爲實現Serializable接口會很容易。設計

  • 除非一個類在用了一段時間以後就會被拋棄,
    • 不然,實現Serializable接口就是個很嚴肅的承諾,必須認真對待。
  • 若是一個類是爲了繼承而設計的,則更加須要加倍當心。
    • 對於這樣的類而言,在「容許子類實現Serializable接口」或「禁止子類實現Serializable接口」二者之間的一個折衷設計方案是,
      • 提供一個可訪問的無參構造器,這種設計方案容許(但不要求)子類實現Serializable接口。
      • 至於爲何須要父類有一個無參的構造器,
        • 是由於子類先序列化自身的時候先調用父類的無參的構造器。 
        • 實例:
          • private void writeObject(java.io.ObjectOutputStream out) 
              throws IOException{ 
               out.defaultWriteObject();//先序列化對象 
               out.writeInt(parentvalue);//再序列化父類的域 
              } 
              private void readObject(java.io.ObjectInputStream in) 
              throws IOException, ClassNotFoundException{ 
               in.defaultReadObject();//先反序列化對象 
                 parentvalue=in.readInt();//再反序列化父類的域 
              }
相關文章
相關標籤/搜索