Serialization converts a Java object into a byte stream for storage or transmission. Deserialization reconstructs the object from bytes.

Basic Serialization

Implement java.io.Serializable (a marker interface):

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

    private String name;
    private int age;
    private transient String password; // excluded from serialization

    public User(String name, int age, String password) {
        this.name = name;
        this.age = age;
        this.password = password;
    }
}
  

Writing and Reading Objects

  User user = new User("Alice", 30, "secret");

// Serialize
try (ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("user.ser"))) {
    oos.writeObject(user);
}

// Deserialize
try (ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("user.ser"))) {
    User restored = (User) ois.readObject();
    System.out.println(restored.getName()); // Alice
    // restored.password is null (transient)
}
  

serialVersionUID

Every serializable class should declare a serialVersionUID:

  private static final long serialVersionUID = 1L;
  

If the class structure changes without updating the UID, deserialization throws InvalidClassException. Increment the UID when making incompatible changes.

transient Keyword

Fields marked transient are skipped during serialization:

  • Passwords and secrets
  • Derived/computed fields
  • Non-serializable objects

Externalizable Interface

For full control over serialization format, implement Externalizable:

  public class Product implements Externalizable {
    private String name;
    private double price;

    public Product() { } // public no-arg constructor required

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeUTF(name);
        out.writeDouble(price);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException {
        name = in.readUTF();
        price = in.readDouble();
    }
}
  

Unlike Serializable, Externalizable requires you to explicitly read and write every field.

Serialization Rules

What gets serialized:

  • Non-transient, non-static instance fields of serializable classes
  • Object graphs (referenced objects must also be serializable)

What does not get serialized:

  • static fields
  • transient fields
  • Fields of non-serializable types (throws NotSerializableException)

Custom Serialization

  public class Order implements Serializable {
    private static final long serialVersionUID = 1L;
    private List<Item> items;

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        // custom logic
    }

    private void readObject(ObjectInputStream in)
            throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        // custom logic
    }
}
  

Security Concerns

Java serialization has known security vulnerabilities (deserialization attacks):

  • Do not deserialize untrusted data
  • Prefer JSON, Protocol Buffers, or other formats for external data exchange
  • Use ObjectInputFilter (Java 9+) to restrict deserializable classes:
  ObjectInputStream ois = new ObjectInputStream(input);
ois.setObjectInputFilter(
    ObjectInputFilter.Config.createFilter("com.myapp.*;!*"));
  

Modern Alternatives

Format Use Case
JSON (Jackson, Gson) REST APIs, config files
Protocol Buffers High-performance RPC
Java Records + JSON Simple DTOs

Best Practices

  • Always declare serialVersionUID
  • Mark sensitive and non-serializable fields as transient
  • Avoid Java serialization for external/untrusted data
  • Consider JSON or Protobuf for new projects
  • Keep serializable classes simple — prefer composition over deep object graphs