Java Serializable Interface
Last modified: April 16, 2025
The java.io.Serializable
interface is a marker interface that enables
object serialization in Java. Serialization converts objects into byte streams
for storage or transmission. Deserialization reconstructs objects from these
byte streams.
Classes implement Serializable
to indicate their instances can be
serialized. The interface has no methods to implement. Serialization handles
primitive types and object graphs automatically. It preserves object references
and circular references.
Serializable Interface Overview
Serialization requires implementing the Serializable
interface. The
process uses ObjectOutputStream
and ObjectInputStream
.
Transient fields are excluded from serialization. Static fields are never
serialized.
public interface Serializable { // Marker interface - no methods }
The code above shows the simple declaration of Serializable
. Despite
being empty, it enables powerful serialization capabilities. The JVM handles all
serialization mechanics automatically when this interface is implemented.
Basic Serialization Example
This example demonstrates basic serialization of a simple object. The
Person
class implements Serializable
. We serialize to
a file and then deserialize back to an object.
import java.io.*; class Person implements Serializable { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } } public class Main { public static void main(String[] args) { Person person = new Person("John Doe", 30); // Serialization try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) { oos.writeObject(person); System.out.println("Serialized: " + person); } catch (IOException e) { e.printStackTrace(); } // Deserialization try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) { Person deserialized = (Person) ois.readObject(); System.out.println("Deserialized: " + deserialized); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }
This example shows complete serialization and deserialization of a Person
object. The serialized data is written to "person.ser". After deserialization,
the object has the same state as before serialization. Note the casting required
when reading the object back.
Transient Fields Example
The transient
keyword excludes fields from serialization. This is
useful for sensitive data or fields that shouldn't persist. Transient fields are
set to default values during deserialization.
import java.io.*; class Account implements Serializable { private String username; private transient String password; // Won't be serialized private double balance; public Account(String username, String password, double balance) { this.username = username; this.password = password; this.balance = balance; } @Override public String toString() { return "Account{username='" + username + "', password='" + (password == null ? "null" : "****") + "', balance=" + balance + "}"; } } public class Main { public static void main(String[] args) { Account account = new Account("johndoe", "secret123", 1000.50); // Serialize try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("account.ser"))) { oos.writeObject(account); System.out.println("Serialized: " + account); } catch (IOException e) { e.printStackTrace(); } // Deserialize try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("account.ser"))) { Account deserialized = (Account) ois.readObject(); System.out.println("Deserialized: " + deserialized); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }
This example demonstrates how transient
fields behave during
serialization. The password field is excluded from serialization. When
deserialized, the password field is null. This protects sensitive data from
being stored in serialized form.
Custom Serialization with readObject and writeObject
For more control over serialization, classes can implement custom methods.
writeObject
and readObject
allow custom serialization
logic. These methods must be private and have specific signatures.
import java.io.*; class Employee implements Serializable { private String name; private transient double salary; // Custom serialization public Employee(String name, double salary) { this.name = name; this.salary = salary; } private void writeObject(ObjectOutputStream oos) throws IOException { oos.defaultWriteObject(); // Serialize non-transient fields oos.writeDouble(salary * 1.1); // Custom serialization logic } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); // Deserialize non-transient fields this.salary = ois.readDouble() / 1.1; // Custom deserialization } @Override public String toString() { return "Employee{name='" + name + "', salary=" + salary + "}"; } } public class Main { public static void main(String[] args) { Employee emp = new Employee("Alice Smith", 50000); // Serialize try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("employee.ser"))) { oos.writeObject(emp); System.out.println("Serialized: " + emp); } catch (IOException e) { e.printStackTrace(); } // Deserialize try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("employee.ser"))) { Employee deserialized = (Employee) ois.readObject(); System.out.println("Deserialized: " + deserialized); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }
This example shows custom serialization for a transient field. The salary is marked transient but still serialized using custom logic. During serialization, the salary is increased by 10%. During deserialization, it's decreased by 10%. This demonstrates how to implement custom serialization logic.
SerialVersionUID for Version Control
serialVersionUID
is a version control mechanism for serialized
classes. It ensures compatibility between serialized objects and class versions.
If not defined, the JVM generates one based on class structure.
import java.io.*; class Product implements Serializable { private static final long serialVersionUID = 1L; // Version identifier private String name; private double price; public Product(String name, double price) { this.name = name; this.price = price; } @Override public String toString() { return "Product{name='" + name + "', price=" + price + "}"; } } public class Main { public static void main(String[] args) { Product product = new Product("Laptop", 999.99); // Serialize try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("product.ser"))) { oos.writeObject(product); System.out.println("Serialized: " + product); } catch (IOException e) { e.printStackTrace(); } // Deserialize try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("product.ser"))) { Product deserialized = (Product) ois.readObject(); System.out.println("Deserialized: " + deserialized); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }
This example demonstrates using serialVersionUID
for version control.
The constant helps maintain compatibility if the class changes. If the UID differs
during deserialization, an InvalidClassException
occurs. Always
explicitly declare serialVersionUID for important serializable classes.
Inheritance and Serialization
Serialization behavior with inheritance requires special consideration. If a superclass is serializable, its fields are serialized automatically. If not, its fields must be handled manually during serialization.
import java.io.*; class Address { private String city; private String country; public Address(String city, String country) { this.city = city; this.country = country; } @Override public String toString() { return city + ", " + country; } } class Customer extends Address implements Serializable { private String name; private transient Address address; // Non-serializable superclass public Customer(String name, String city, String country) { super(city, country); this.name = name; this.address = new Address(city, country); } private void writeObject(ObjectOutputStream oos) throws IOException { oos.defaultWriteObject(); oos.writeObject(address.toString()); // Serialize superclass state } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); String addrStr = (String) ois.readObject(); String[] parts = addrStr.split(", "); this.address = new Address(parts[0], parts[1]); } @Override public String toString() { return "Customer{name='" + name + "', address=" + address + "}"; } } public class Main { public static void main(String[] args) { Customer customer = new Customer("Bob", "New York", "USA"); // Serialize try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("customer.ser"))) { oos.writeObject(customer); System.out.println("Serialized: " + customer); } catch (IOException e) { e.printStackTrace(); } // Deserialize try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("customer.ser"))) { Customer deserialized = (Customer) ois.readObject(); System.out.println("Deserialized: " + deserialized); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }
This example shows handling inheritance with serialization. The Address
superclass isn't serializable, so we handle it manually. The Customer
class implements custom serialization for the superclass fields. This ensures all
necessary data is properly serialized and deserialized.
Serializing Collections
Java collections can be serialized if their elements are serializable. This
includes ArrayList
, HashMap
, and other standard
collections. The entire collection structure is preserved during serialization.
import java.io.*; import java.util.ArrayList; import java.util.List; class Student implements Serializable { private String name; private int id; public Student(String name, int id) { this.name = name; this.id = id; } @Override public String toString() { return "Student{name='" + name + "', id=" + id + "}"; } } public class Main { public static void main(String[] args) { List<Student> students = new ArrayList<>(); students.add(new Student("Alice", 101)); students.add(new Student("Bob", 102)); students.add(new Student("Charlie", 103)); // Serialize try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students.ser"))) { oos.writeObject(students); System.out.println("Serialized: " + students); } catch (IOException e) { e.printStackTrace(); } // Deserialize try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("students.ser"))) { @SuppressWarnings("unchecked") List<Student> deserialized = (List<Student>) ois.readObject(); System.out.println("Deserialized: " + deserialized); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }
This example demonstrates serializing an ArrayList
of
Student
objects. The entire collection structure is preserved.
Each element must implement Serializable
. The deserialized
collection is identical to the original, maintaining all elements and order.
Source
Java Serializable Interface Documentation
In this article, we've covered the essential aspects of the Java
Serializable
interface. Understanding serialization is crucial for
persisting objects and transmitting them across networks in Java applications.
Author
List all Java tutorials.