Master Core Java Programming From Scratch

Clear, interactive, and structured coding lessons designed for absolute beginners.

Serialization in Java

Learn how Java objects can be represented as a byte stream, how serialization and deserialization work, and how to use Serializable, transient fields, serialVersionUID, and safer alternatives for application data.

What is Serialization?

Serialization is the process of converting an object's state into a byte stream so that it can be stored or transmitted.

Deserialization is the reverse process: reconstructing an object from a serialized representation.

Concept
Object
   ↓
Serialization
   ↓
Byte Stream
   ↓
Storage / Transfer
   ↓
Deserialization
   ↓
Object

Serializable Interface

A class can implement java.io.Serializable to indicate that its instances can participate in Java's built-in object serialization mechanism.

Java
import java.io.Serializable;

class Student implements Serializable {

    private String name;

    private int age;

}
Important: Serializable is a marker interface. It does not require implementing methods.

ObjectOutputStream

ObjectOutputStream writes serializable objects to an output stream.

Java
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

Student student =
    new Student();

try (
    ObjectOutputStream output =
        new ObjectOutputStream(
            new FileOutputStream(
                "student.ser"
            )
        )
) {

    output.writeObject(student);

}

ObjectInputStream

ObjectInputStream can reconstruct objects from a serialized stream.

Java
import java.io.FileInputStream;
import java.io.ObjectInputStream;

try (
    ObjectInputStream input =
        new ObjectInputStream(
            new FileInputStream(
                "student.ser"
            )
        )
) {

    Student student =
        (Student) input.readObject();

}

Complete Serialization Example

Java
import java.io.*;

class Student implements Serializable {

    private static final long
        serialVersionUID = 1L;

    private String name;

    private int age;

    Student(String name, int age) {

        this.name = name;
        this.age = age;

    }

    public String getName() {

        return name;

    }

    public int getAge() {

        return age;

    }

}

Writing the Object

Java
Student student =
    new Student(
        "Amit",
        22
    );

try (
    ObjectOutputStream output =
        new ObjectOutputStream(
            new FileOutputStream(
                "student.ser"
            )
        )
) {

    output.writeObject(student);

}

Reading the Object

Java
try (
    ObjectInputStream input =
        new ObjectInputStream(
            new FileInputStream(
                "student.ser"
            )
        )
) {

    Student student =
        (Student) input.readObject();

    System.out.println(
        student.getName()
    );

    System.out.println(
        student.getAge()
    );

}

serialVersionUID

serialVersionUID is a version identifier used during Java serialization compatibility checks.

Java
private static final long
    serialVersionUID = 1L;

Explicitly declaring it makes the intended serialization version easier to control.

InvalidClassException

During deserialization, incompatible class version information can result in InvalidClassException.

Java
try {

    ObjectInputStream input =
        new ObjectInputStream(
            new FileInputStream(
                "student.ser"
            )
        );

    Student student =
        (Student) input.readObject();

}
catch (InvalidClassException exception) {

    System.out.println(
        "Incompatible serialized class"
    );

}

transient Keyword

A field marked transient is skipped by the default Java serialization mechanism.

Java
class User implements Serializable {

    private String username;

    private transient String password;

}
Security Note: Do not treat transient as a general security mechanism. Sensitive data should be handled using appropriate application-level security practices.

Static Fields and Serialization

Static fields belong to the class rather than to an individual object, so they are not part of an object's serialized instance state.

Java
class Counter implements Serializable {

    private static int count;

    private int value;

}

The instance field value can participate in serialization, while the static field count does not represent per-object serialized state.

Object Graph Serialization

Serialization can traverse referenced objects as part of an object graph, provided the relevant referenced objects are serializable.

Java
class Address implements Serializable {

    private String city;

}

class Employee implements Serializable {

    private String name;

    private Address address;

}
If a non-transient referenced object is not serializable, serialization can fail with NotSerializableException.

NotSerializableException

If the serialization mechanism encounters a non-transient field whose runtime object is not serializable, Java can throw NotSerializableException.

Java
try {

    output.writeObject(employee);

}
catch (NotSerializableException exception) {

    System.out.println(
        "Object cannot be serialized"
    );

}

Custom Serialization Hooks

A serializable class can define private writeObject() and readObject() methods to customize the default serialization process.

Java
private void writeObject(
    ObjectOutputStream output
) throws IOException {

    output.defaultWriteObject();

}

private void readObject(
    ObjectInputStream input
) throws IOException,
         ClassNotFoundException {

    input.defaultReadObject();

}

Externalizable

Externalizable provides more explicit control over serialization by requiring implementations of writeExternal() and readExternal().

Java
class Student
    implements Externalizable {

    public Student() {
    }

    public void writeExternal(
        ObjectOutput output
    ) throws IOException {

        // write fields explicitly

    }

    public void readExternal(
        ObjectInput input
    ) throws IOException,
             ClassNotFoundException {

        // read fields explicitly

    }

}

Externalizable is a specialized mechanism and requires careful handling of the serialization contract.

Serialization and Inheritance

If a serializable subclass extends a non-serializable superclass, the superclass portion is not restored through default serialization. The first accessible no-argument constructor of the non-serializable superclass is involved during deserialization.

Java
class Person {

    protected String name;

}

class Student
    extends Person
    implements Serializable {

    private int age;

}

Serialization Security

Java native deserialization can be dangerous when untrusted serialized data is accepted because deserialization can invoke class-specific behavior and reconstruct complex object graphs.

Security Rule: Avoid deserializing untrusted Java serialization streams. Prefer safer data formats and explicit validation for untrusted external data.

Alternatives to Native Serialization

Modern applications often use explicit data formats such as JSON or other protocol formats instead of Java native serialization for external communication.

Approach Typical Use
Java Serialization Legacy/internal object persistence scenarios
JSON REST APIs and human-readable data exchange
Protocol Buffers Efficient structured service communication
Database Persistence Long-term structured application data

Java Serialization vs JSON

Feature Java Serialization JSON
Format Java-specific binary format Text format
Language Interoperability Limited Excellent
Human Readable No Yes
External Input Requires strong security controls Still requires validation

Example: Saving Student Data 🤓👨‍🏫

Java
class Student implements Serializable {

    private static final long
        serialVersionUID = 1L;

    private String name;

    private int age;

    Student(String name, int age) {

        this.name = name;
        this.age = age;

    }

}
Java
Student student =
    new Student(
        "Neha",
        21
    );

try (
    ObjectOutputStream output =
        new ObjectOutputStream(
            new FileOutputStream(
                "student.ser"
            )
        )
) {

    output.writeObject(student);

}

System.out.println(
    "Student saved"
);

Serialization Best Practices

  • Use try-with-resources for serialization streams.
  • Declare serialVersionUID explicitly when using Java serialization.
  • Mark fields that should not be serialized as transient when appropriate.
  • Avoid native Java deserialization of untrusted data.
  • Prefer explicit data-transfer formats for external APIs.
  • Consider whether long-term persistence should use a database or a stable application-level data format.

Interview Questions

Serialization converts an object's state into a byte stream that can be stored or transmitted.

Deserialization reconstructs an object from a serialized representation.

It is a version identifier used by Java's serialization mechanism when checking class compatibility during deserialization.

A transient field is excluded from default Java serialization.

No. Native Java deserialization of untrusted input can create serious security risks. Untrusted data should use safer formats and strong validation.
Summary

Java serialization converts object state into a byte stream and deserialization reconstructs objects from that stream. Important concepts include Serializable, ObjectOutputStream, ObjectInputStream, serialVersionUID, transient fields, object graphs, custom serialization, and the security risks of native deserialization.