代码改变世界

Effective Java 76 Write readObject methods defensively

2014-05-10 15:20  小郝(Kaibo Hao)  阅读(560)  评论(0编辑  收藏  举报

Principle

  1. readObject method is effectively another public constructor, and it demands all of the same care as any other constructor. Just as a constructor must check its arguments for validity (Item 38) and make defensive copies of parameters where appropriate (Item 39), so must a readObject method.
  2. When an object is deserialized, it is critical to defensively copy any field containing an object reference that a client must not possess.
  3. Do not use the writeUnshared and readUnshared methods.
  4. If it is not comfortable adding a public constructor that took as parameters the values for each nontransient field in the object and stored the values in the fields with no validation, you must provide a readObject method, and it must perform all the validity checking and defensive copying that would be required of a constructor. Alternatively, you can use the serialization proxy pattern(Item 78).

/**

* @author Kaibo Hao Immutable class that uses defensive copying

*/

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;

}

   

// Remainder omitted

   

// readObject method with defensive copying and validity checking

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);

}

}  

Guidelines for writing a bulletproof readObject method

  1. For classes with object reference fields that must remain private, defensively copy each object in such a field. Mutable components of immutable classes fall into this category.
  2. Check any invariants and throw an InvalidObjectException if a check fails. The checks should follow any defensive copying.
  3. If an entire object graph must be validated after it is deserialized, use the ObjectInputValidation interface [JavaSE6, Serialization].
  4. Do not invoke any overridable methods in the class, directly or indirectly.  

Summary

Anytime you write a readObject method, adopt the mind-set that you are writing a public constructor that must produce a valid instance regardless of what byte stream it is given. Do not assume that the byte stream represents an actual serialized instance.