Serialization and deserialization

Files so far held text and raw bytes. But your program's data lives in objects: customers, carts, game progress. Serialization turns a whole object, with everything it points to, into bytes you can save or send, and deserialization rebuilds it later. You'll learn how to opt in, how to keep secrets out with transient, what serialVersionUID protects, and why reading serialized data from strangers is dangerous.

Module 13 · File Handling and I/O⏱ 24 minIntermediate9 practice problems
1 The story

The cart that vanished

Riya's canteen app lets students build a cart before paying. If the app closes, the cart disappears and students have to add everything again. She could write each item as text, but the cart has items, quantities, a coupon and the student's details, all objects pointing to objects.

Cart item item serialize AC ED 00 05 73 72 00 04 43 61 … cart.ser deserialize Cart item item myphatshala.in
Riya

Sir, can't I just save the whole cart object as it is?

Arjun Sir

You can. Mark the class Serializable and an ObjectOutputStream will flatten the cart and everything it points to into bytes.

Riya

And the student's UPI PIN that's stored in there?

Arjun Sir

That must never reach a file. Mark it transient. Let's see exactly what gets written.

2 Definition

What are serialization and deserialization?

Formal definition (write this in exams)

Serialization is the process of converting an object's state, together with the objects it references (its object graph), into a sequence of bytes that can be stored or transmitted. Deserialization reconstructs new objects with the same state from those bytes. In Java, a class opts in by implementing the marker interface java.io.Serializable; objects are written with ObjectOutputStream.writeObject() and read with ObjectInputStream.readObject(). Fields declared transient and static fields are not serialized. A serialVersionUID identifies the version of the class and is checked during deserialization; a mismatch causes InvalidClassException.

In simple words

Serialization packs an object flat into a box of bytes. Deserialization unpacks the box and builds an identical object, a twin, not the original.

Words you'll meet in this lesson

SerializableMarker interface: “objects of this class may be serialized”.
Object graphAn object plus everything reachable from its reference fields.
transientField modifier: skip this field when serializing.
serialVersionUIDA long that labels the class version stored in the bytes.
Magic numberEvery stream begins with AC ED 00 05.
PersistenceKeeping data after the program ends.

What gets saved?

FieldSaved?After reading
Normal instance field (int points)YesSaved value
Reference to a Serializable objectYes, the whole objectA new equal object
Reference to a non-Serializable object—NotSerializableException while writing
transient fieldNoDefault: null, 0, false
static fieldNoWhatever the class has now

Exam tip: “Serializable is a marker interface. transient and static fields are not serialized. Deserialization does not call the constructor of a serializable class. Declare private static final long serialVersionUID to control version compatibility.”

3 Types and ways to use it

The rules in one class

class Customer implements Serializable {          // 1. opt in (marker interface)
    private static final long serialVersionUID = 1L; // 2. version number of the format
    String name;                                     //    saved
    int points;                                      //    saved
    Address home;                                    // 3. must be Serializable too
    transient String otp;                            // 4. skipped, comes back null
    static int count;                                // 5. class data, never saved
}

Default serialization

Implement Serializable; Java writes all non-transient fields. Enough for most cases.

writeObject

transient

Leave out secrets (PINs, passwords), caches and things that can't be serialized (open files, threads).

skip a field

Custom writeObject/readObject

Private methods in your class to encrypt, validate or convert fields yourself.

advanced

JSON instead

For APIs and long-term storage, text formats (JSON with Jackson/Gson) are readable, language-neutral and safer.

common today

When the class changes

If you serialize with one version of a class and read with another whose serialVersionUID differs (or, if you didn't declare one, whose computed UID changed because you edited the class), reading fails:

java.io.InvalidClassException: Customer; local class incompatible:
    stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

Never deserialize data you don't trust. readObject can create objects of any serializable class on your classpath, without running constructors. Attackers have used crafted byte streams to run code on servers. Accept JSON (validated) from outside, or use deserialization filters (ObjectInputFilter) to allow only expected classes.

4 Step by step

What writeObject and readObject do

  1. Open an object stream

    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cart.ser"));
    It wraps a byte stream and immediately writes the header AC ED 00 05.
  2. writeObject(cart)

    It checks the class implements Serializable, writes a class description (name, serialVersionUID, field names and types), then the values of each non-transient, non-static field.
  3. Follow references

    For each reference field it serializes the referenced object the same way, recursively. Objects already written in this stream are stored as a short back-reference, so cycles and shared objects work.
  4. readObject()

    Cart cart = (Cart) in.readObject();
    It reads the class description, checks the UID, allocates a new object without calling its constructor, and sets the fields from the stream. Transient fields keep default values.
  5. Cast and use

    readObject returns Object; you cast it. It throws ClassNotFoundException if the class isn't available, and InvalidClassException if the versions don't match.
5 See it happen

Object to bytes and back: both views

Left: a Customer saved to a file and read back later. Right: how serialization walks an object graph and keeps a shared object shared.

Serialization, both views 6 steps
What you see Customer c = new Customer("Asha", 120); out.writeObject(c); // ObjectOutputStream // customer.ser on disk // the program ends: c is gone Customer back = (Customer) in.readObject(); back == c ? // false: a new object Customer c name "Asha" points 120 otp "4821" (transient) (object gone) customer.ser (bytes) AC ED 00 05 73 72 … class name, fields: "Asha", 120 (no otp) Customer back "Asha", 120 otp = null a new object with the same state; not c itself An object in memory. Serialize: object → bytes. Bytes saved in a file. Program ends; memory is gone. Deserialize: bytes → object. Same data, new object. myphatshala.in
Behind the screen HEAP (before) #1 Customer home → #2 work → #2 #2 Address city "Pune" stream / file Customer: class + fields Address #2: "Pune" home again: handle → #2 HEAP (after read) #3 Customer home, work → #4 #4 Address "Pune" static fields: not saved An object graph. Write the Customer. Follow its reference: Address. Seen before: just a handle. Read: new objects, same shape. Shared stays shared. myphatshala.in
6 Real-world use

Where serialization is used

Java serialization still appears in caches, sessions and older systems, and the same idea, turning objects into bytes, is everywhere in modern formats such as JSON and Protocol Buffers.

Watch it with an example: a shopping cart that survives an app restart

A shopping app saves the customer's cart when the app is closed and restores it when it opens again.

Save the cart, restore the cart 5 steps
Animated example Cart (in memory) phone cover Rs 299 charger Rs 799 earphones Rs 1499 app closed cart.ser 412 bytes Cart (restored) 3 items, Rs 2597 coupon: re-checked Cart with 3 items. App closing: writeObject. App closed. App opens: readObject. Restored and validated. myphatshala.in

1. Web sessions across servers

Some servers serialize HTTP session objects (logged-in user, cart) to share them across machines or survive restarts.

session→serialize→other server

2. Caches

Distributed caches store objects as bytes; Java serialization or a faster format turns them into bytes and back.

product object→bytes→cache server

3. Saving app state

Games, desktop tools and mobile apps save progress and drafts so users can continue later.

quit game→writeObject(progress)→save.dat
{"id":1}

4. APIs (with JSON)

REST APIs serialize Java objects to JSON text and deserialize requests back into objects: the same idea in a portable, safer format.

Order object→Jackson→JSON response

Simplified. New systems usually prefer JSON or Protocol Buffers over Java's built-in serialization, mainly for safety and compatibility with other languages.

7 Run the code

Save a customer, load a twin

import java.io.*;

class Customer implements Serializable {
    private static final long serialVersionUID = 1L;
    String name;
    int points;
    transient String otp;

    Customer(String name, int points, String otp) {
        this.name = name;
        this.points = points;
        this.otp = otp;
    }
}

public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Customer c = new Customer("Asha", 120, "4821");
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("customer.ser"))) {
            out.writeObject(c);
        }
        System.out.println("saved " + new File("customer.ser").length() + " bytes");
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("customer.ser"))) {
            Customer back = (Customer) in.readObject();
            System.out.println(back.name + ", " + back.points + " points, otp=" + back.otp);
            System.out.println("same object? " + (back == c));
        }
    }
}
saved 77 bytes Asha, 120 points, otp=null same object? false
  • implements SerializableWithout it, writeObject throws NotSerializableException.
  • transient String otpNot written; comes back as null.
  • (Customer) in.readObject()Returns Object, so cast. Throws ClassNotFoundException (checked) if the class is missing.
  • back == cfalse: deserialization always creates new objects.
8 Line by line

A Customer saved and restored

Recorded from the real JVM. Watch a second Customer object appear on the heap after readObject: same name and points, OTP null, different object number.

Worked examples: real problems, solved and traced in memory

Three cases you'll meet: saving game progress, protecting a secret with transient, and saving a whole list of objects in one call. Watch new objects appear on the heap when they are read back.

Example 1Gaming · progress

Save and load game progress

Problem statement

A quiz game must save the player's progress (name, level, score, list of badges) when they quit and restore it next time. Make the class serializable, save it, load it, and show the restored progress.

Input
Ravi, level 7, score 4350, badges: Speed, Streak
Expected output
Ravi is on level 7 with 4350 points
badges: [Speed, Streak]

Approach

  1. Progress implements Serializable, with a serialVersionUID.
  2. Fields include an ArrayList<String>, which is itself serializable.
  3. Write with ObjectOutputStream, read with ObjectInputStream, cast the result.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 2Banking · security

Never save the PIN: transient

Problem statement

A banking app caches a Session object to disk so a user doesn't have to re-select their account. The session holds the account number, the last screen and the user's PIN. The PIN must never be written to disk.

Input
account XX4521, screen statements, PIN 7391
Expected output
file mentions account? true
file mentions PIN?     false
restored: XX4521, statements, pin=null

Approach

  1. Mark pin as transient.
  2. Serialize, then look inside the raw bytes for the PIN text to prove it's absent.
  3. Deserialize and show the PIN came back as null.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 3E-commerce · catalogue

Save a whole product list in one call

Problem statement

A shop caches its product catalogue (a list of Product objects) to a file so it can start quickly without waiting for the server. Save the list with one writeObject call, reload it, and print the cheapest product.

Input
Rice 5kg Rs 450, Oil 1L Rs 165, Sugar 1kg Rs 48
Expected output
3 products loaded; cheapest: Sugar 1kg Rs 48

Approach

  1. Product implements Serializable.
  2. ArrayList is serializable, so writing the list writes every product in it.
  3. Read it back and cast to List<Product> (an unchecked cast, so add @SuppressWarnings).

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

9 Quiz and revision

Check yourself

Quick revision

Lesson 13.4 in one screen

  • Serialization: object graph → bytes. Deserialization: bytes → new objects.
  • Opt in with the marker interface Serializable; every object in the graph must be serializable.
  • ObjectOutputStream.writeObject, ObjectInputStream.readObject (returns Object; cast it).
  • transient and static fields are not saved; transient comes back as the default value.
  • Constructors of serializable classes don't run on deserialization.
  • Declare serialVersionUID; a mismatch gives InvalidClassException.
  • Never deserialize untrusted data; prefer JSON for external data.

Interview and viva questions

What is serialization, and why is Serializable a marker interface?

Serialization converts an object's state into bytes for storage or transfer. Serializable has no methods: it only marks a class as permitted, so classes don't get serialized (and their internals exposed) by accident.

What is the use of the transient keyword?

It excludes a field from serialization. Use it for secrets (passwords, PINs), derived or cached values that can be recomputed, and fields whose types aren't serializable (threads, streams, connections). After deserialization the field has its default value.

What is serialVersionUID and what happens if you don't declare it?

It's a version number stored in the stream and checked when reading. If you don't declare one, Java computes it from the class's structure, so small changes (adding a method or field) change it and old files can no longer be read (InvalidClassException). Declaring it explicitly keeps compatible changes working.

Is the constructor called during deserialization?

Not for the serializable class or its serializable superclasses. The JVM calls only the no-arg constructor of the first non-serializable superclass (often Object), then restores fields from the stream. Validation in constructors is therefore bypassed.

What are the risks of Java serialization?

Deserializing untrusted data can instantiate unexpected classes and trigger “gadget chains” leading to remote code execution. It's also Java-only, fragile across class changes, and exposes private fields in the bytes. Mitigate with ObjectInputFilter allow-lists, or use JSON/Protocol Buffers with validation.

Practice problems

Serialize objects, control what's saved with transient, and handle the exceptions. Each program writes and reads its own file. The last three are business tasks.

P1Easy

Save and load one object

Make a serializable Book (title, price). Save one and load it back.

Input
Java Basics, 399
Expected output
Java Basics Rs 399
Show solution and explanation
import java.io.*;

class Book implements Serializable {
    private static final long serialVersionUID = 1L;
    String title;
    int price;
}

public class Main {
    public static void main(String[] args) throws Exception {
        Book b = new Book();
        b.title = "Java Basics";
        b.price = 399;
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("book.ser"))) {
            out.writeObject(b);
        }
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("book.ser"))) {
            Book copy = (Book) in.readObject();
            System.out.println(copy.title + " Rs " + copy.price);
        }
    }
}
Java Basics Rs 399

How it works: Serializable is a marker interface: no methods, it just gives permission. readObject returns Object, so cast it.

P2Easy

transient fields come back empty

Serialize a Login with a username and a transient password and print both after loading.

Input
asha / secret123
Expected output
user=asha, password=null
Show solution and explanation
import java.io.*;

class Login implements Serializable {
    private static final long serialVersionUID = 1L;
    String user = "asha";
    transient String password = "secret123";
}

public class Main {
    public static void main(String[] args) throws Exception {
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("login.ser"))) {
            out.writeObject(new Login());
        }
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("login.ser"))) {
            Login l = (Login) in.readObject();
            System.out.println("user=" + l.user + ", password=" + l.password);
        }
    }
}
user=asha, password=null

How it works: transient fields get default values after deserialization (null, 0, false). Initializers like = "secret123" don't run for a deserialized object.

P3Easy

The magic number

Serialize a String and print the first four bytes of the file in hex.

Input
"hello"
Expected output
AC ED 00 05 (12 bytes)
Show solution and explanation
import java.io.*;
import java.nio.file.*;

public class Main {
    public static void main(String[] args) throws IOException {
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("s.ser"))) {
            out.writeObject("hello");
        }
        byte[] b = Files.readAllBytes(Path.of("s.ser"));
        System.out.printf("%02X %02X %02X %02X (%d bytes)%n", b[0], b[1], b[2], b[3], b.length);
    }
}
AC ED 00 05 (12 bytes)

How it works: every Java serialization stream starts with the magic number AC ED and version 00 05. Security tools look for these bytes in network traffic.

P4Medium

Constructors don't run

Show that deserialization does not call the class's constructor: count constructor calls in a static counter.

Input
(none)
Expected output
seat 14, constructor calls: 1
Show solution and explanation
import java.io.*;

class Ticket implements Serializable {
    private static final long serialVersionUID = 1L;
    static int made = 0;
    int seat;
    Ticket(int seat) { this.seat = seat; made++; }
}

public class Main {
    public static void main(String[] args) throws Exception {
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("t.ser"))) {
            out.writeObject(new Ticket(14));
        }
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("t.ser"))) {
            Ticket t = (Ticket) in.readObject();
            System.out.println("seat " + t.seat + ", constructor calls: " + Ticket.made);
        }
    }
}
seat 14, constructor calls: 1

How it works: the JVM creates the object without running Ticket's constructor and then fills in the fields from the stream. So any validation in your constructor is skipped; that's one reason deserializing untrusted data is risky.

P5Medium

Static fields aren't saved

Serialize an object, change a static field, deserialize, and show the static value is the current one, not the saved one.

Input
(none)
Expected output
user=meena, theme=dark
Show solution and explanation
import java.io.*;

class Config implements Serializable {
    private static final long serialVersionUID = 1L;
    static String theme = "light";
    String user = "meena";
}

public class Main {
    public static void main(String[] args) throws Exception {
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cfg.ser"))) {
            out.writeObject(new Config());
        }
        Config.theme = "dark";
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("cfg.ser"))) {
            Config c = (Config) in.readObject();
            System.out.println("user=" + c.user + ", theme=" + Config.theme);
        }
    }
}
user=meena, theme=dark

How it works: serialization saves an object's state; static fields belong to the class, so they're not part of it.

P6Medium

Catch NotSerializableException

A serializable Order has a field of a non-serializable type Printer. Catch the exception and print which class caused it.

Input
(none)
Expected output
cannot save: Printer is not Serializable
Show solution and explanation
import java.io.*;

class Printer {
    String model = "TP-80";
}

class Order implements Serializable {
    private static final long serialVersionUID = 1L;
    int id = 501;
    Printer printer = new Printer();
}

public class Main {
    public static void main(String[] args) throws IOException {
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("o.ser"))) {
            out.writeObject(new Order());
        } catch (NotSerializableException e) {
            System.out.println("cannot save: " + e.getMessage() + " is not Serializable");
        }
    }
}
cannot save: Printer is not Serializable

How it works: the whole object graph must be serializable. Fix it by making Printer serializable, or by marking the field transient and recreating it after loading.

P7Business problem

Persist a customer list

Save three customers (name, city, loyalty points) as one list, reload it, and print the total points.

Input
3 customers
Expected output
3 customers, 495 points in total
Show solution and explanation
import java.io.*;
import java.util.*;

class Customer implements Serializable {
    private static final long serialVersionUID = 1L;
    String name, city;
    int points;
    Customer(String n, String c, int p) { name = n; city = c; points = p; }
}

public class Main {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) throws Exception {
        List<Customer> list = new ArrayList<>(List.of(
            new Customer("Asha", "Pune", 120), new Customer("Ravi", "Nagpur", 300), new Customer("Imran", "Pune", 75)));
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("customers.ser"))) {
            out.writeObject(list);
        }
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("customers.ser"))) {
            List<Customer> loaded = (List<Customer>) in.readObject();
            int total = 0;
            for (Customer c : loaded) total += c.points;
            System.out.println(loaded.size() + " customers, " + total + " points in total");
        }
    }
}
3 customers, 495 points in total

How it works: collections serialize their elements, so one call saves everything. Remember the cast from Object is unchecked: the file could contain anything.

P8Business problem

Shared address saved once

Two employees share one office Address object. Serialize both in one stream and show they still share the same Address after loading.

Input
2 employees, 1 office
Expected output
same office object? true, city Mumbai
Show solution and explanation
import java.io.*;

class Address implements Serializable {
    private static final long serialVersionUID = 1L;
    String city = "Mumbai";
}

class Employee implements Serializable {
    private static final long serialVersionUID = 1L;
    String name;
    Address office;
    Employee(String n, Address a) { name = n; office = a; }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Address hq = new Address();
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("staff.ser"))) {
            out.writeObject(new Employee("Asha", hq));
            out.writeObject(new Employee("Ravi", hq));
        }
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("staff.ser"))) {
            Employee a = (Employee) in.readObject();
            Employee r = (Employee) in.readObject();
            System.out.println("same office object? " + (a.office == r.office) + ", city " + r.office.city);
        }
    }
}
same office object? true, city Mumbai

How it works: within one stream, an object that was already written is stored as a back-reference, so sharing is preserved. Objects must be read in the same order they were written.

P9Business problem

Version check with serialVersionUID

Print the serialVersionUID that Java uses for a class, so a team can fix it explicitly before shipping.

Input
class Invoice
Expected output
Invoice serialVersionUID = 202610
fields saved: 2
Show solution and explanation
import java.io.*;

class Invoice implements Serializable {
    private static final long serialVersionUID = 2026_10L;
    String number;
    double amount;
}

public class Main {
    public static void main(String[] args) {
        ObjectStreamClass desc = ObjectStreamClass.lookup(Invoice.class);
        System.out.println(desc.getName() + " serialVersionUID = " + desc.getSerialVersionUID());
        System.out.println("fields saved: " + desc.getFields().length);
    }
}
Invoice serialVersionUID = 202610 fields saved: 2

How it works: the UID is stored in every serialized stream. When reading, Java compares it with the current class; if they differ you get InvalidClassException. Declaring it yourself keeps compatible changes (like adding a field) from breaking old files.

Real-world scenario questions (asked in company interviews)

Serialization questions test both mechanics and judgement: when to use it, and when not to.

Release · compatibilityJunior

After the update, saved carts won't load

A desktop billing app saves open bills with Java serialization. After a release that added one field to Bill, every saved bill fails with InvalidClassException: local class incompatible. What happened, and how do you prevent it?

The interviewer is checking: serialVersionUID.

Show a strong answer
  • Bill had no declared serialVersionUID, so Java computed one; adding a field changed it and old streams no longer match.
  • Declare private static final long serialVersionUID from the first release. Adding a field is then a compatible change: old data loads with the new field at its default.
  • For this release, re-add the old computed UID (find it with the serialver tool) to recover existing files.

Avoid: telling users to delete their saved bills.

Security · data leakFresher

Card numbers found in session files

A security audit finds full card numbers inside the server's serialized session files on disk. The session object holds a PaymentInfo field. What would you change?

The interviewer is checking: transient and handling sensitive data.

Show a strong answer
  • Serialization writes every non-transient field in plain bytes; there's no encryption.
  • Don't keep card data in the session at all: store a token from the payment gateway. If a field must exist, mark it transient.
  • Encrypt session storage and restrict file permissions.

Avoid: making the field private and assuming that hides it; private fields are serialized too.

Security · attackMid

An endpoint that accepts serialized objects

An internal API receives orders as Java-serialized objects in the HTTP body and calls readObject(). The team wants to expose it to partners. What's your advice?

The interviewer is checking: deserialization vulnerabilities.

Show a strong answer
  • Deserializing attacker-controlled bytes can instantiate arbitrary classes from the classpath and lead to remote code execution.
  • Switch the API to JSON with a strict schema and validation; it's also language-neutral for partners.
  • If Java serialization must stay, apply an ObjectInputFilter allow-list of expected classes, size limits, and authentication.

Avoid: “we only accept our Order class”. The cast happens after readObject has already built the objects.

Design · graphsMid

Serialized files are huge and slow

A Customer object is cached by serializing it, but each file is several MB. Investigation shows Customer has a reference to a shared CatalogService that holds the entire product catalogue. Explain and fix.

The interviewer is checking: object graphs and transient.

Show a strong answer
  • Serialization follows every reference, so the whole catalogue is written with each customer.
  • Mark service references transient and re-inject them after loading, or serialize a small DTO with only the customer's data.

Avoid: compressing the files and ignoring the design problem.