第11章 序列化

来源:互联网 发布:2016怎么做好淘宝 编辑:程序博客网 时间:2024/05/19 05:06

条款 74:谨慎实现Serializable接口

1 实现Serializable接口的代价:

A 一旦该类被发布,大大降低了该类改变的灵活性

实现Serializable接口后,字节流编码成了导出API的一部分;

如果接受了默认序列化形式,则类中私有和包级私有的实例域都成为导出API的一部分,不能信息隐藏

类的演变受到限制:UID

B 增加了bug和漏洞的可能性

反序列化机制是一个隐藏的构造器

依靠默认的反序列化机制,很容易使对象约束关系遭到破坏

C 测试负担增加

既要确保“序列化-反序列化”成功,也要确保结果产生的对象真正是原始对象的复制品

2 应该实现Serializable接口的情况:

A 某类加入到某框架中,且该框架依赖序列化,则该类实现序列化

B 某类成为其它类组件,且后者必须实现Serializable接口,则该类序列化后更容易被后者使用

C Date/BigInteger/大多数集合应该序列化

D 静态成员类

3 不应实现Serializable接口的情况:、

A 代表活动实体的类,如线程池,不该实现Serializable接口

B 为了继承而设计的类不该,除了Throwable类、Component类、HttpServlet类。

在为继承而设计的类中,允许/禁止子类实现Serializable接口之间的折中方案是:提供一个可访问无参构造器。

C 内部类不该,内部类默认序列化形式不清楚

 

条款 75:考虑自定义序列化形式

1 如果类实现了Serializable接口且使用默认序列化形式,则永远无法摆脱原来的实现。

2 即使确定了默认序列化形式是合适的,最好提供一个readObject方法保证约束关系和安全性

3 @serial标签

4 当某对象物理表示法和逻辑表示有很大区别时,使用默认序列化很大问题:

A 该类导出的API永远束缚在该类内部表示法上

B 消耗空间

C 消耗时间

D 容易栈溢出

5 readObject要先调用defaultReadObject,writeObject要先调用defaultWriteObject

6 如果使用默认序列化且域为transient,则:

A 实例被反序列化时,这些域被初始化为默认值

B 如果默认值不能被transient接受,则必须提供readObject方法;或者,这些域延迟到使用时初始化。

7 UID生成:手动写;运行serialver工具

 

条款 76:保护性编写readObject方法

1 readObject是一个用 字节流 作为 唯一 参数的构造器。

2 安全考虑的话,可以先在该方法内调用defaultReadObject方法,然后检查反序列化后对象是否有效。如果有效性检查失败,抛出InvalidObjectException异常。

3 恶意攻击:

    字节流以一个有效的Period实例开头,然后附上非法引用,引用指向实例中私有域。

  • 提供一个显式readObject方法,执行构造器要求的所有有效性检查和保护性拷贝。
  • 使用序列化代理 serialization proxy pattern 

4 建议:

1) 对于必须保持私有的对象引用域,防御性的复制该域所保存的对象。

2) 对于有不变性的类,检查不变性,如果不满足的话,就抛出InvalidObjectException。检查跟在防御性复制之后。

3) 如果在反序列化之后整个对象图需要进行验证,可以使用ObjectInputValidation接口。

4) 不在方法中直接或间接的调用任何可被重载的方法。

 

条款 77:对于实例控制,枚举优先于readResolve

1 readObject方法,不管是显式还是默认,都返回一个新建的实例,该实例不是类初始化时的实例。

2 readResolve方法允许用readObject创建的新实例来代替另一个实例。

3 当对象引用域的内容被反序列化时,允许一个“非法”流盗用指向最初反序列化Singleton的引用。

4 尽量用枚举来实施实例控制的约束条件,如果不能又需要一个既可序列化又可实例受控的类,则提供一个readResolver方法并确保该类所有实例域都是基本类型/transient.

 

条款 78:序列化代理

1 在目标类内嵌一个 静态 类,称为序列化代理。内嵌类有一个构造器,从外围类拷贝数据。两个类都需要声明实现Serializable接口。

2 在外围类新建writeReplace方法,方法内调用内嵌类,进行序列化代理。这样,永远不会产生外围类的序列化实例。

3 在外围类添加readObject方法,直接抛出InvalidObjectException异常,目的是让外围类该方法不可用,防止被攻击。

4 内嵌类添加readResolve方法,返回一个逻辑上等同于外围类的实例。这样,序列化系统在反序列化时将序列化代理转变回外围类实例。

5 序列化代理功能比保护性拷贝更大:允许反序列化实例有着与原始序列化实例不同的类。

6 序列化代理局限:

  1. 不能与可被客户端拓展的类兼容,也不能与对象图中包含循环的某些类兼容。
  2. 代价和开销不小。
原创粉丝点击