A Deep Dive into the Serializable Interface and Serialization Process

Introduction: The Illusion of a Single Word

Have you ever added implements Serializable just to fix an error and moved on without knowing why it was needed or how it worked?

Let’s understand what Serializable means, when it matters, and how it's different from the JSON serialization you see in everyday APIs.

But Isn’t It Just a Marker Interface?

All we know is that Serializable is a marker interface—it has no methods, no fields, no obvious behavior. But is that understanding enough?

How does adding a single word implements Serializable to your class enable so much power behind the scenes? Where is the actual logic? The answer lies in Java's core serialization framework in the java.io package.

These I/O classes check whether an object is instanceof Serializable and then treat it differently. So while your class shows no behavior, this marker acts like a green flag to say: "Hey JVM, it's okay to serialize me."

This one keyword — though invisible in runtime logic — signals the JVM to activate serialization behavior handled by trusted, low-level code in Java’s own libraries.

In simpler terms, Java’s standard I/O classes are built to check for this marker. If your class doesn’t implement Serializable, it throws a NotSerializableException. But if it does, Java uses reflection and built-in mechanisms to convert your object to bytes and reconstruct it later.

Analogy:

Think of implements Serializable as a sticker on a box that says:

“This box is safe to ship!”

The Java I/O system is like a shipping company. It sees the sticker and says:

“Great! I know how to wrap this up and send it across the wire.”

But if that sticker isn’t there, it refuses to handle it.

So, while you don’t write any methods in Serializable, the mere presence of this marker unlocks a powerful system already waiting in the background.

The Hidden Agreement Behind Serialization

When your class implements Serializable, it's silently entering into an agreement with Java's serialization engine. This agreement is enforced by the Java I/O libraries and acts like a contract that both sides must respect.

Think of it this way: The marker interface doesn’t do the work — it just shows that you’ve signed the contract*.*

The actual work — like converting objects to bytes, tracking object references, skipping transient fields, and reconstructing the object during deserialization — is all done by Java’s core I/O classes such as ObjectOutputStream and ObjectInputStream.

These classes are part of the standard Java library and are built to respond differently if your object implements Serializable.

So, while your class doesn’t gain any behavior explicitly, the Java runtime kicks in with a whole machinery of logic once that marker is present. That’s how a single keyword unlocks so much behavior.

Serialization Agreement Summary

Types of Serialization: Clearing the Confusion

Before diving into when serialization happens, it’s important to clarify that Java developers often encounter two very different types of serialization — Java Serialization, JSON Serialization and they are not the same — even though both involve “converting objects”.

Object Vs JSON Serialization

Rule of Thumb

Think of Java Serialization as for internal system use (sessions, caches, disk). Think of JSON Serialization as for data communication with clients (REST, APIs).

Understanding this distinction helps avoid confusion, especially when an error says “serialization failed.”

The fix is very different depending on which type of serialization is in play.

When Does Java Serialization Come Into the Picture?

Serialization typically appears when:

  • You’re storing an object in an HTTP session (e.g., Spring Session + Redis)

  • You use distributed caches (e.g., Hazelcast, Ehcache)

  • You send objects over a network.

  • You write an object to a file.

  • You enqueue/dequeue objects using message queues.

Even though your controller returns JSON, under the hood, tools like Redis or Kafka may serialize the object before storing or transferring it.

Important: Returning an object from a REST controller does not use Java serialization*. It uses* Jackson to convert Java objects to JSON.

So How Does It Work Under the Hood?

So we have seen the actual logic comes from Java’s core libraries, specifically the ObjectOutputStream and ObjectInputStream classes in the java.io package. These classes check whether an object is instanceof Serializable and then handle serialization or deserialization accordingly.

Now let’s see how this works in practice.

What Is serialVersionUID?

serialVersionUID is a special field used during Java serialization and deserialization to ensure version compatibility of classes.

private static final long serialVersionUID = 1L;

When Java serializes an object, it stores this ID alongside the object. During deserialization, Java checks whether the serialVersionUID in the stream matches the one in the current class definition.

  • If they match: Deserialization proceeds.

  • If they don’t match: You get an InvalidClassException, meaning the class definition has changed in a way that could break compatibility.

If you don’t define it manually, Java auto-generates one based on the class structure, which may change even with minor edits (like adding a field). That’s why defining it explicitly helps ensure safe deserialization over time.

Think of serialVersionUID as a version fingerprint for your class.

When you try to serialize an object:

  1. Java checks: if (object instanceof Serializable)

  2. If not, it throws NotSerializableException

  3. If yes, it recursively writes fields to a byte stream using reflection

  4. Metadata like class name and serialVersionUID is stored

But what’s actually written to the byte stream?

Inside the Byte Stream:

To illustrate this, let’s consider an Employee class:

public class Employee implements Serializable {
    private int id;
    private String name;
    private String email;
    private String department;
    private List<String> roles;
}

When you serialize an Employee object with some sample data, Java converts it into a structured stream of bytes. Here's what it typically includes:

  • Stream Header: Java writes a magic number (0xAC ED) and version to signal the beginning of a serialized stream.

  • Class Descriptor: Contains the fully qualified class name, serialVersionUID, and field metadata (name and type).

  • Field Values: Written in the order declared in the class. Java serializes primitives directly and recursively serializes objects.

  • List Handling: For collections like List<String> roles, Java serializes the list implementation (e.g., ArrayList), then each item in order.

  • Reference Handles: Java assigns reference IDs to track repeated or shared objects, avoiding duplication and infinite loops.

Visualizing the Byte Stream

Example: Serializing Employee(1, "Alice", "alice@xyz.com", "Engineering", List.of("ADMIN", "USER")) -> and employee.ser is created.

Serialized Object as Seen in a Hexdump

Breakdown of What You’re Seeing:

  • Left Column (e.g., 00000000): These are byte offsets in hexadecimal. Think of them like line numbers in the binary file.

  • Middle (e.g., ac ed 00 05): Actual byte values—each pair is one byte.

  • ac ed → Magic number indicating the start of a serialized stream.

  • 73 72 → TC_CLASSDESC type code (class descriptor follows).

  • Right (|....sr..Employee|): ASCII view. Non-printable characters show as .

And now the object is ready:

Once all this binary content is written and saved:

  • The object is fully serialized.

  • It includes everything needed to reconstruct the object: class name, field types, values, etc.

  • You can now send this data across the network, store it in a database, or read it back with ObjectInputStream.

Deserialization reads this byte stream, validates the structure, and uses Java’s internal APIs to reconstruct the object — without calling its constructor!

Java Serialization is not magic — it’s structured binary logic, and you just saw it.

Why Serializable Isn’t Always the Best Choice

  • It tightly couples objects with JVM internals

  • Incompatible between different JVM versions or languages

  • Often replaced in distributed systems with:

  • JSON (Jackson, Gson)

  • Protocol Buffers

  • Avro / Thrift

  • Serialization can be a security risk if readObject() is overridden carelessly

Conclusion: One Word, Many Responsibilities

Adding implements Serializable might look like a formality, but it’s the entry point into one of Java’s oldest and most powerful mechanisms. It allows your objects to move beyond memory and live in files, caches, networks, and clustered systems.

But with that power comes the need to understand how it works, when it breaks, and when you should look for better alternatives.

Serialization isn’t just about saving objects — it’s about knowing when they should be saved, how, and why.

0
Subscribe to my newsletter

Read articles from Shamika Kshirsagar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Shamika Kshirsagar
Shamika Kshirsagar