第74条:谨慎地实现Serializable接口:
第75条:考虑使用自定义的序列化形式:
public final class StringList implements Serializable {
private transient int size = 0;
private transient Entry head = null;
// No longer Serializable!
private static class Entry {
String data;
Entry next;
Entry previous;
}
// Appends the specified string to the list
public final void add(String s) {
// Implementation omitted
}
/**
* Serialize this {@code StringList} instance.
*
* @serialData The size of the list (the number of strings it contains) is
* emitted ({@code int}), followed by all of its elements (each
* a {@code String}), in the proper sequence.
*/
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeInt(size);
// Write out all elements in the proper order.
for (Entry e = head; e != null; e = e.next)
s.writeObject(e.data);
}
private void readObject(ObjectInputStream s) throws IOException,
ClassNotFoundException {
s.defaultReadObject();
int numElements = s.readInt();
// Read in all elements and insert them in list
for (int i = 0; i < numElements; i++)
add((String) s.readObject());
}
private static final long serialVersionUID = 93248094385L;
// Remainder omitted
}
第76条:保护性地编写readObject方法:
public final class Period implements Serializable {
private Date start;
private 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;
}
// readObject method with defensive copying and validity checking - Page 306
// This will defend against BogusPeriod and MutablePeriod attacks.
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
// Defensively copy our mutable components
start = new Date(start.getTime());
end = new Date(end.getTime());
// Check that our invariants are satisfied
if (start.compareTo(end) > 0)
throw new InvalidObjectException(start + " after " + end);
}
}
第77条:对于实例控制,枚举类型优先于readResolve
还是那句话,单例模式用enum实现最好。
第78条:考虑用序列化代理代替序列化实例,序列化最佳实践:
首先,为可序列化类设计一个私有的静态嵌套类,精确表示外围类的实例的逻辑状态,被称作序列化代理。它有一个单独构造器,参数类型就是外围类。外围类和序列代理都实现Serializable接口。
其次,外围类增加writeReplace方法,返回序列代理对象
再次,外围类增加readObject方法,直接抛异常,防止攻击
最后,序列代理类提供readResolve方法,返回逻辑上相当的外围类实例。
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
// 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");
}
}
本人博客已搬家,新地址为:http://yidao620c.github.io/