第78条:考虑用序列化代理代替序列化实例

为可序列话的类设计一个私有的静态嵌套类,精确地表示外围类的实例的逻辑状态,这样就可以做到只传递数据,不能通过修改数据来使用类中的方法。具体实现如下

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Date;

public final class Period implements Serializable {
	private static final long serialVersionUID = 1280496190504379879L;

	private final Date start;
	private final Date end;

	private static class SerializationProxy implements Serializable {

		private static final long serialVersionUID = 4989353667150495079L;

		private final Date start;
		private final Date end;

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

		private Object readResolve() {
			return new Period(this.start, this.end);
		}
	}

	// 使用writeReplace来让序列化系统产生一个SerializationProxy实例
	private Object writeReplace() {
		return new SerializationProxy(this);
	}

	// 不允许直接反序列Period
	private void readObject(ObjectInputStream stream) throws InvalidObjectException {
		throw new InvalidObjectException("Proxy required");
	}

	/**
	 * @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) {
		if (start.compareTo(end) > 0)
			throw new IllegalArgumentException(start + " after " + end);
		this.start = new Date(start.getTime());
		this.end = new Date(end.getTime());
	}

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

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

	@Override
	public String toString() {
		return this.start + " - " + this.end;
	}
	
	public static void main(String[] args) {
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectOutputStream out = new ObjectOutputStream(bos);
			out.writeObject(new Period(new Date(), new Date()));
			
			ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
			Period period = (Period) in.readObject();
			System.out.println(period);
		} catch (Exception e) {
			throw new AssertionError(e);
		}
	}
}