1. Core Concepts & Architecture

Definition

Serialization is the process of saving an object’s state only, not its behavior (methods are not serialized). It converts an object into a sequence of bytes to be stored in a file/database or transmitted over a network.

Serialization Engine

  • Handled by the java.io.ObjectOutputStream class, which acts as a filter stream wrapped around a lower-level byte stream.
  • Triggered by invoking ObjectOutputStream.writeObject(saveThisObject).

Deserialization Engine

  • Handled by java.io.ObjectInputStream.
  • Triggered by invoking ObjectInputStream.readObject(), which reads bytes from persistence, reconstructs the object, and returns a generic Object requiring explicit type casting (prone to ClassCastException).
sequenceDiagram
    autonumber
    participant App as Application
    participant OOS as ObjectOutputStream
    participant JVM as Serialization Runtime
    participant Target as Target Object

    App->>OOS: writeObject(obj)
    OOS->>JVM: Verify Serializable & check for custom writeObject()
    alt Custom writeObject defined
        JVM->>Target: Execute private writeObject(out)
    else Default Mechanism
        JVM->>Target: Serialize fields (skip transient/static)
    end
    JVM-->>App: Byte Stream Output

2. Compatibility & Eligibility

Class Eligibility

  • Serializable: Most standard Java classes (Strings, arrays, collections, AWT/Swing GUI components) implement Serializable.

  • Non-Serializable: System-level resources like Thread, OutputStream (and subclasses), and Socket are not serializable.

  • Inheritance Rule: If a child class implements Serializable but its parent class does not, the parent must have a public or protected no-arg default constructor.

    • Non-inherited fields follow standard serialization rules.

    • Inherited fields from non-serializable parents are initialized using the parent’s default constructor during deserialization.

3. Important Methods & Interfaces

Key Methods Involved

  • writeObject() and readObject()

  • writeExternal() and readExternal()

Serializable vs. Externalizable

Featurejava.io.Serializablejava.io.Externalizable
ControlAutomatic (default mechanism).Full programmatic control via developer.
MethodsMarker interface (no methods).Must implement writeExternal(ObjectOutput) and readExternal(ObjectInput).
PerformanceSlower due to reflection overhead.Faster because fields are written/read manually.

4. Variables & Modifiers

Static and Transient Behavior

  • Static variables belong to the class, not the instance, and are not serialized (except for serialVersionUID).

  • Transient variables are explicitly skipped by the serialization mechanism. During deserialization, they re-initialize to default primitive values (0, false) or null for objects.

5. Exceptions & Versioning

Common Exceptions

NotSerializableException

Thrown when a serializable class holds a reference to a non-serializable field (e.g., an Employee class containing an un-serializable Address field).

InvalidClassException

Thrown when local class definitions are incompatible with the serialized stream, usually due to a serialVersionUID mismatch.

The Role of serialVersionUID

  • A 64-bit hash representing class structure[cite: 1].

  • If you modify a class (e.g., add a new field) without a fixed serialVersionUID, the JVM generates a new runtime hash[cite: 1]. If you try to deserialize an older byte stream, the hashes will mismatch and throw an InvalidClassException[cite: 1].

Best Practice

Always declare an explicit serialVersionUID

private static final long serialVersionUID = 1L;

6. Advanced Customization Techniques

A. Customizing Default Protocol (writeObject / readObject)

You can hook into the default process by defining private methods[cite: 1]. The JVM checks for these methods via reflection during serialization[cite: 1].

private void writeObject(ObjectOutputStream out) throws IOException {
    out.defaultWriteObject(); // Performs standard serialization first[cite: 1]
    // Add custom logic here (e.g., encryption, extra headers)[cite: 1]
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject(); // Performs standard deserialization first[cite: 1]
    // Add custom logic here (e.g., re-initialize transient states, start threads)[cite: 1]
}

B. Blocking Serialization in a Subclass

If your superclass is serializable but you want to prevent a subclass from being serialized, override the private methods to throw a NotSerializableException[cite: 1]:

private void writeObject(ObjectOutputStream out) throws IOException {
    throw new NotSerializableException("Serialization is disabled for this secure subclass.");
}
private void readObject(ObjectInputStream in) throws IOException {
    throw new NotSerializableException("Deserialization is disabled for this secure subclass.");
}

7. Modern Security Update (Added Context)

Security Vulnerability Notice

Native Java serialization is a frequent vector for Remote Code Execution (RCE) when reading untrusted streams via ObjectInputStream.readObject().

  • Mitigation (Java 9+): Use ObjectInputFilter to validate incoming classes before they are deserialized, or avoid native serialization altogether in modern microservices in favor of safe text/binary protocols like JSON (Jackson/Gson) or Protocol Buffers.