Serialization & deserialization
Serializable, ObjectOutput/InputStream, transient, serialVersionUID, the object graph.
Persisting a whole object
Serialization turns a live object — and everything it references — into a byte stream you can write to a file or send over a socket. Deserialization reverses it: read the bytes back and reconstruct an equal object graph. This is how the Greenhouse project implements *save and restore*: when a fault occurs it serializes the entire GreenhouseControls object to dump.out, and later the Restore program reads dump.out back to resume exactly where it left off.
The four pieces
implements Serializable— a *marker* interface (no methods). It is permission: "the JVM may serialize instances of this class." Without it, trying to serialize throwsNotSerializableException.ObjectOutputStream.writeObject(obj)— writes the object graph.ObjectInputStream.readObject()— reads it back; returnsObject, so you cast to the real type.- The object graph —
writeObjectfollows every reference and serializes the *whole* reachable graph (and de-duplicates shared objects), so each referenced object must itself beSerializable.
### Writing
GreenhouseControls gc = new GreenhouseControls();
try (ObjectOutputStream out =
new ObjectOutputStream(new FileOutputStream("dump.out"))) {
out.writeObject(gc); // whole object graph -> bytes
}
### Reading back
try (ObjectInputStream in =
new ObjectInputStream(new FileInputStream("dump.out"))) {
GreenhouseControls gc = (GreenhouseControls) in.readObject();
}
readObject() does not call the class's constructor — it rebuilds the object's fields directly from the stream. (Any logic you keep only in a constructor, like a timer initialized from the clock, will *not* re-run on restore.)
transient: fields you do NOT save
Mark a field transient to exclude it from serialization. Use it for (a) data that should not persist (a password, a cache), and (b) fields whose type is not serializable. On deserialization a transient field comes back as its default (0, false, null).
private String name; // serialized
private transient Socket conn; // skipped; comes back null
serialVersionUID: the version stamp
Each serializable class carries a serialVersionUID — a version number written into the stream. On read, the JVM compares the stream's UID to the loaded class's UID; a mismatch throws InvalidClassException. If you do not declare one, the compiler *computes* it from the class's exact shape, so any change (add a field, change a signature) silently breaks old data. Declare it explicitly so you control compatibility:
private static final long serialVersionUID = 1L;