Memra

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

  1. implements Serializable — a *marker* interface (no methods). It is permission: "the JVM may serialize instances of this class." Without it, trying to serialize throws NotSerializableException.
  2. ObjectOutputStream.writeObject(obj) — writes the object graph.
  3. ObjectInputStream.readObject() — reads it back; returns Object, so you cast to the real type.
  4. The object graphwriteObject follows every reference and serializes the *whole* reachable graph (and de-duplicates shared objects), so each referenced object must itself be Serializable.

### 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;
NORMAL ~/memra/learn/comp-308/serialization utf-8 LF