1. The Serializable Interface
Remember the examples from the previous lecture? Java classes that we want to serialize must implement a special interface — java.io.Serializable. This is a so-called marker interface: it does not contain any methods and simply “marks” a class as suitable for serialization. If a class implements this interface, the JVM allows its objects to be serialized using standard tools.
It’s not advisable to serialize everything indiscriminately, because not all objects can or should be saved as bytes. Some objects depend on the operating system state, open files, or network connections. Therefore, Java requires you to explicitly mark a class as serializable.
A marker interface is like a sticker saying “approved for packing” on a box. If there’s no such sticker, the packer (JVM) refuses to work.
Example: declaring a serializable class
import java.io.Serializable;
public class User implements Serializable {
private String name;
private int age;
// Constructor, getters, and setters
public User(String name, int age) {
this.name = name;
this.age = age;
}
// For convenience: toString() method
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
Note:
- We simply added implements Serializable to the class declaration.
- No methods need to be implemented (the interface is empty).
- All standard Java classes that can be serialized (for example, ArrayList, HashMap, String) already implement Serializable.
2. How to Make Your Class Serializable
Rule No. 1: Just add implements Serializable
That’s all that’s required for the class itself. But there are caveats!
Important: all referenced objects must also be serializable.
If your class has fields that reference other objects, they must be serializable as well. For example:
public class Profile implements Serializable {
private User user; // User must be serializable!
private int level;
}
If at least one field is not serializable, an exception will be thrown when attempting serialization.
3. Example of Serialization and Deserialization
Let’s see how to serialize and deserialize an object to a file. For this, use the classes ObjectOutputStream and ObjectInputStream.
Example: serializing a User object to a file
import java.io.*;
public class SerializeDemo {
public static void main(String[] args) {
User user = new User("Alice", 30);
// Save the object to a file
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
oos.writeObject(user);
System.out.println("Object successfully serialized to file user.ser");
} catch (IOException e) {
System.out.println("Serialization error: " + e.getMessage());
}
}
}
What’s happening here?
- Create a User object.
- Open an ObjectOutputStream that writes to the file "user.ser".
- Call writeObject(user). At this moment, the JVM converts the object into a stream of bytes and saves it to the file.
Example: deserializing an object from a file
import java.io.*;
public class DeserializeDemo {
public static void main(String[] args) {
// Read the object from a file
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
User user = (User) ois.readObject();
System.out.println("Object successfully restored: " + user);
} catch (IOException | ClassNotFoundException e) {
System.out.println("Deserialization error: " + e.getMessage());
}
}
}
What’s happening here?
- Open an ObjectInputStream that reads from the file "user.ser".
- Call readObject(). The JVM restores the object from bytes.
- Don’t forget to cast the result to the required type (User), because readObject() returns Object.
- ClassNotFoundException may occur if the User class is not found during deserialization.
All together: serialization and deserialization
import java.io.*;
public class SerializationExample {
public static void main(String[] args) {
User user = new User("Bob", 22);
// Serialization
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
oos.writeObject(user);
System.out.println("Serialization completed!");
} catch (IOException e) {
System.out.println("Serialization error: " + e.getMessage());
}
// Deserialization
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
User loaded = (User) ois.readObject();
System.out.println("Deserialization completed! " + loaded);
} catch (IOException | ClassNotFoundException e) {
System.out.println("Deserialization error: " + e.getMessage());
}
}
}
Output:
Serialization completed!
Deserialization completed! User{name='Bob', age=22}
4. What Happens “Under the Hood” During Serialization
When you call writeObject, the JVM first checks whether the class implements the Serializable interface. If the class is not marked as serializable, an exception is thrown. The JVM then iterates over all ordinary fields of the object (those that are not static and not transient) and writes their values to the byte stream. If any of these fields are other objects, serialization is applied to them recursively, but only if they also implement Serializable.
During deserialization, the object is created without invoking the usual constructor, and its fields are populated with the saved values— as if a “constructor without a constructor” brings the object back to life from the byte stream.
Some fields will not be serialized. Static fields (static) belong to the class itself rather than an individual object, so their values are not saved. Fields marked transient are skipped as well—this is convenient for temporary data, caches, or sensitive information such as passwords.
Serialization Process Diagram
flowchart TB
A[User object in memory] -- writeObject --> B[ObjectOutputStream]
B -- saves bytes --> C[user.ser file]
C -- readObject --> D[ObjectInputStream]
D -- restores --> E[User object in memory]
5. Common Pitfalls When Using Serializable
Pitfall No. 1: Field referencing a non-serializable object. If the User class has a field of type, for example, Thread or Socket, serialization will fail. Not all objects can be serialized—keep this in mind!
Pitfall No. 2: Non-serializable inner classes. If the User class contains an inner class that is not static, serialization may fail. It’s better to use static nested classes or separate top-level classes.
Pitfall No. 3: Attempting to serialize a static field. Static fields are not serialized—they belong to the class, not the object. After deserialization, a static field will have the value defined in the class, not the serialized object.
Pitfall No. 4: Class version mismatch. If you change the class structure after serialization (for example, add or remove a field) and then try to deserialize an old object, an InvalidClassException may occur. To control versions, use the special field serialVersionUID—we’ll talk about it in more detail in the next lecture.
GO TO FULL VERSION