文章目录
  1. 1. 谨慎地实现Serializable接口
  2. 2. 考虑使用自定义的序列化形式
  3. 3. 保护性地编写readObject方法
  4. 4. 对于实例控制,枚举类型优先于readResolve
  5. 5. 考虑用序列化代理代替序列化实例

将一个对象编码成一个字节流,称作将该对象序列化;相反的处理过程被称为反序列化。

谨慎地实现Serializable接口

实现Serializable接口的代价:

  1. 一旦一个类被发布,就大大降低了“改变这个类的实现”的灵活性;
  2. 增加了出现Bug和安全漏洞的可能性;
  3. 随着类发行新的版本,相关的测试负担也增加了

为了继承而设计的类应尽可能少地去实现Serializable接口。

内部类不应该实现Serilizable。

考虑使用自定义的序列化形式

如果没有先认真考虑默认的序列化形式是否合适,则不要贸然接受。

如果一个对象的物理表示法等同于它的逻辑内容,可能就适合于使用默认的序列化形式。

即使确定了默认的序列化形式是合适的,通常还必须提高一个readObject方法以保证约束关系和安全性。

当一个对象的物理表示法与它的逻辑数据内容有实质性的区别时,使用默认序列化形式会有如下缺点:

  1. 使这个类的导出API永远地束缚在该类的内部表示法上;
  2. 消耗过多空间;
  3. 消耗过多时间;
  4. 引起栈溢出;

不管选择哪种序列化形式,都要为自己编写的每个可序列化的类声明一个显式的序列版本UID。

保护性地编写readObject方法

readObject方法相当于另一个公有的构造器,如同其他构造器一样,它也需要注意同样的问题,需要保护性的编写该方法。

对于实例控制,枚举类型优先于readResolve

任何readObject方法都会返回一个新建的实例;readResolve特性允许使用readObject创建的实例代替另一个实例。

应该尽可能地泗洪枚举类型来实施实例控制的约束条件,如果做不到,同时又需要一个既可以序列化又是实例受控的类,就必须提高一个readResolve方法,并确保该类的所有实例域都为基本类型,或者是transient的。

考虑用序列化代理代替序列化实例

public final class Period implements Serializable {
    private final Date start;
    private final Date end;

    /**
     * @param start
     *            the beginning of the period
     * @param end
     *            the end of the period; must not precede start
     * @throws IllegalArgumentException
     *             if start is after end
     * @throws NullPointerException
     *             if start or end is null
     */
    public Period(Date start, Date end) {
        this.start = new Date(start.getTime());
        this.end = new Date(end.getTime());
        if (this.start.compareTo(this.end) > 0)
            throw new IllegalArgumentException(start + " after " + end);
    }

    public Date start() {
        return new Date(start.getTime());
    }

    public Date end() {
        return new Date(end.getTime());
    }

    public String toString() {
        return start + " - " + end;
    }

    // Serialization proxy for Period class - page 312
    private static class SerializationProxy implements Serializable {
        private final Date start;
        private final Date end;

        SerializationProxy(Period p) {
            this.start = p.start;
            this.end = p.end;
        }

        private static final long serialVersionUID = 234098243823485285L; // Any
                                                                            // number
                                                                            // will
                                                                            // do
                                                                            // (Item
                                                                            // 75)

        // readResolve method for Period.SerializationProxy - Page 313
        private Object readResolve() {
            return new Period(start, end); // Uses public constructor
        }
    }

    // writeReplace method for the serialization proxy pattern - page 312
    private Object writeReplace() {
        return new SerializationProxy(this);
    }

    // readObject method for the serialization proxy pattern - Page 313
    private void readObject(ObjectInputStream stream)
            throws InvalidObjectException {
        throw new InvalidObjectException("Proxy required");
    }
}

代理模式有两个局限性:

  1. 不能与被客户端扩展的类兼容;
  2. 性能稍微降低;
文章目录
  1. 1. 谨慎地实现Serializable接口
  2. 2. 考虑使用自定义的序列化形式
  3. 3. 保护性地编写readObject方法
  4. 4. 对于实例控制,枚举类型优先于readResolve
  5. 5. 考虑用序列化代理代替序列化实例