Disadvantage of Serializable
- A major cost of 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, the
class‘s private and package-private instance fields become part of its
exported API, and the practice of minimizing access to fields (Item
13) loses its effectiveness as a tool for information hiding.It is possible to change the internal
representation while maintaining the original serialized form (usingObjectOutputStream.putFields and
ObjectInputStream.readFields ), but it can be difficult and leaves visible
warts in the source code.stream unique identifiers (serial
version UIDs)If you do not specify this number explicitly by
declaring a static final long field named serialVersionUID , the system
automatically generates it at runtime by applying a complex procedure to the
class.The automatically generated value is affected
by the class‘s name, the names of the interfaces it implements, and all ofits public and protected members. If you fail
to declare an explicit serial version UID, compatibility will be broken,
resulting in an InvalidClassException at runtime. - A second cost of implementing Serializable is
that it increases the likelihood of bugs and security holes.Objects are created using constructors;
serialization is an extralinguistic mechanism for creating objects. Relying on
the default deserialization mechanism can easily leave objects open to
invariant corruption and illegal access (Item 76). - A third cost of implementing Serializable is
that it increases the testing burden associated with releasing a new version
of a class.When a serializable class is revised, it is
important to check that it is possible to serialize an instance in the new
release and deserialize it in old releases, and vice versa.
Principle
- Classes designed for inheritance (Item
17) should rarely implement Serializable, and interfaces should rarely
extend it. - If the class has invariants that would be
violated if its instance fields were initialized to their default values (zero
for integral types, false for boolean, and null for object reference types),
you must add this readObjectNoData method to the class.// readObjectNoData for stateful
extendable serializable classesprivate void readObjectNoData() throws
InvalidObjectException {throw new InvalidObjectException("Stream data
required");}
If a class that is designed for inheritance is
not serializable, it may be impossible to write a serializable subclass.
Specifically, it will be impossible if the superclass does not provide an
accessible parameterless constructor.
You should consider providing a
parameterless constructor on nonserializable classes designed for inheritance .
// Nonserializable stateful class allowing
serializable subclass
public abstract class AbstractFoo {
private int x, y; //
Our state
// This enum and field
are used to track initialization
private enum State {
NEW, INITIALIZING, INITIALIZED };
private
final AtomicReference<State> init = new
AtomicReference<State>(State.NEW);
public AbstractFoo(int
x, int y) { initialize(x, y); }
// This constructor
and the following method allow
// subclass‘s
readObject method to initialize our state.
protected
AbstractFoo() { }
protected final void
initialize(int
x, int y) {
if
(!init.compareAndSet(State.NEW, State.INITIALIZING))
throw new
IllegalStateException("Already initialized");
this.x = x;
this.y = y;
... // Do anything
else the original constructor did
init.set(State.INITIALIZED);
}
// These methods
provide access to internal state so it can
// be manually
serialized by subclass‘s writeObject method.
protected final int
getX() {
checkInit(); return x; }
protected final int
getY() { checkInit(); return y;
}
// Must call
from all public and protected instance methods
private void
checkInit() {
if
(init.get() != State.INITIALIZED)
throw
new IllegalStateException("Uninitialized");
}
... // Remainder
omitted
}
// Serializable subclass of nonserializable
stateful class
public class Foo extends
AbstractFoo implements Serializable {
private void
readObject(ObjectInputStream s)
throws IOException,
ClassNotFoundException {
s.defaultReadObject();
// Manually
deserialize and initialize superclass state
int x =
s.readInt();
int y =
s.readInt();
initialize(x,
y);
}
private void
writeObject(ObjectOutputStream s)
throws IOException {
s.defaultWriteObject();
// Manually
serialize superclass state
s.writeInt(getX());
s.writeInt(getY());
}
// Constructor
does not use the fancy mechanism
public Foo(int x, int
y) { super(x, y); }
private static
final long serialVersionUID = 1856835860954L;
}
Inner classes(Item
22) should not implement Serializable. A static member class can,
however, implement Serializable.
Summary
Unless a class is to be thrown away after a short
period of use, implementing Serializable is a serious commitment that should be
made with care. Extra caution is warranted if a class is designed for
inheritance. For such classes, an intermediate design point between implementing
Serializable and prohibiting it in subclasses is to provide an
accessible parameterless constructor. This design point permits, but
does not require, subclasses to implement Serializable.
Effective Java 74 Implement Serializable judiciously