Serialization

Basic Java
≈ 2 min read

An object as a sequence of bytes

Java's built-in serialization can write an object's state to a byte stream and later restore an object from that stream. For the standard mechanism, a class is usually marked with the marker interface Serializable — it has no methods and only tells the serialization mechanism that the operation is allowed.

class User implements Serializable {
    String name;
    int age;
}

Writing is performed through ObjectOutputStream, while reading is performed through ObjectInputStream. Not only the object itself but also the object graph reachable from it is serialized, provided the corresponding values also support serialization.

Writing and reading

try (ObjectOutputStream out = new ObjectOutputStream(
        new FileOutputStream("user.bin"))) {
    out.writeObject(user);
}
try (ObjectInputStream in = new ObjectInputStream(
        new FileInputStream("user.bin"))) {
    User restored = (User) in.readObject();
}

Deserialization creates a new object based on the data in the stream. The normal constructors of the serializable class are not the same mechanism used to restore its state, so serialization should not be thought of as simply running new again.

serialVersionUID and transient

The serialVersionUID field identifies the version of a class's serialized form. If compatibility of that form is broken, reading older data may fail with InvalidClassException. An explicit value makes the developer's intent clearer.

class Session implements Serializable {
    private static final long serialVersionUID = 1L;

    String user;
    transient String token;
}

A transient field is not stored by the default serialization mechanism. Static fields are not state of a particular object either and are not written as its instance data.

Do not deserialize untrusted data. Using ObjectInputStream on untrusted input is dangerous: processing a specially crafted stream can have serious consequences. If built-in serialization must cross a trust boundary, deserialization filters and strict limits on allowed classes and resource usage are required.

For new external protocols and long-term storage, explicit data formats are usually preferred, with schema and compatibility rules controlled separately. Built-in serialization is more commonly encountered in existing Java code or in tasks where both sides are fully controlled by one application.