谨慎的使用 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();//再反序列化父类的域 
              }
相关文章
相关标签/搜索