Effective Java – Part 6

Chapter 10. Serialization

Item 54: Implement Serializable judiciously
Implementing Serializable is that it decreases the flexibility to change a class’s implementation once it has been released. If you accept the default serialized form and later change the class’s internal representation,an incompatible change in the serialized form may result.
You should carefully design a high-quality serialized form that you are willing to live with for the long haul (Item 55).
Every serializable class has a unique identification number associated with it.
Implementing Serializable increases the likelihood of bugs and security holes.
Relying on the default deserialization mechanism can easily leave objects open to invariant corruption and illegal access (Item 56).
Implementing Serializable increases the testing burden associated with releasing a new version of a class.
Implementing the Serializable interface is not a decision to be undertaken lightly.
Classes designed for inheritance (Item 15) should rarely implement Serializable, and interfaces should rarely extend it.
Consider providing a parameterless constructor on nonserializable classes designed for inheritance.
Inner classes (Item 18) should rarely, if ever, implement Serializable.

Item 55:Consider using a custom serialized form
Do not accept the default serialized form without first considering whether it is appropriate.The ideal serialized form of an object contains only the logical data represented by the object.The default serialized form is likely to be appropriate if an object’s physical representation is identical to its logical content.
Even if you decide that the default serialized form is appropriate, you often must provide a readObject method to ensure invariants and security.

Using the default serialized form when an object’s physical representation differs substantially from its logical data content has four disadvantages:

  1. It permanently ties the exported API to the internal representation.
  2. It can consume excessive space.
  3. It can consume excessive time.
  4. It can cause stack overflows.

If all instance fields are transient, it is technically permissible to dispense with invoking defaultWriteObject and defaultReadObject, but it is not recommended.
Regardless of what serialized form you choose, declare an explicit serial version UID in every serializable class you write.

Item 56:Write readObject methods defensively
When an object is deserialized, it is critical to defensively copy any field containing an object reference that a client must not possess.
Guidelines for writing a bulletproof readObject method:

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

The readResolve method may be used as an alternative to a defensive readObject method.

Item 57: Provide a readResolve method when necessary
If the class of an object being deserialized defines a readResolve method with the proper declaration, this method is invoked on the newly created object after it is deserialized. A readResolve method is necessary not only for singletons, but for all other instance controlled classes. A second use for the readResolve method is as a conservative alternative to the defensive readObject method recommended in Item 56. The accessibility of the readResolve method is significant.

Leave a Comment