Serialization with Inheritance and Object References
Serialization saves more than single objects. It follows inheritance, saving fields from serializable superclasses, and it follows references, saving every object an object points to. Two rules surprise many developers. First, if a superclass is not serializable, its fields aren't saved; instead its no-argument constructor runs during deserialization. Second, an object referenced twice is saved only once, so shared references and even cycles are restored exactly. In this lesson, you will see both rules in action.
Rule 1: Superclasses
| Superclass | Its fields after deserialization |
|---|---|
Implements Serializable | Saved and restored |
Doesn't implement Serializable | Not saved; set by the superclass's no-argument constructor, which must exist and be accessible |
Example: A Non-Serializable Parent
Let's see an example where the parent class isn't serializable.
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class ParentDemo {
static class Person {
String school = "unknown";
Person() {
System.out.println("Person() called");
}
}
static class Student extends Person implements Serializable {
String name;
int marks;
Student(String name, int marks, String school) {
this.name = name;
this.marks = marks;
this.school = school;
}
}
public static void main(String[] args)
throws IOException, ClassNotFoundException {
Student s = new Student("Advitha", 85, "City High");
System.out.println("Before: " + s.name + ", " + s.marks
+ ", " + s.school);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
try (ObjectOutputStream out = new ObjectOutputStream(bytes)) {
out.writeObject(s);
}
try (ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(bytes.toByteArray()))) {
Student copy = (Student) in.readObject();
System.out.println("After: " + copy.name + ", " + copy.marks
+ ", " + copy.school);
}
}
}Person() called
Before: Advitha, 85, City High
Person() called
After: Advitha, 85, unknownExplanation: Person isn't serializable, so its field school wasn't saved. During deserialization, Java ran Person() again, which set school to "unknown". The Student fields, name and marks, were restored normally.
Rule 2: Shared References and Cycles
Serialization gives each object a number the first time it writes it. If the same object appears again, only a back-reference to that number is written. This keeps shared objects shared and lets cycles (an object that eventually refers back to itself) work without looping forever.
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class SharedDemo {
static class Teacher implements Serializable {
String name;
List<Student> students = new ArrayList<>();
Teacher(String name) { this.name = name; }
}
static class Student implements Serializable {
String name;
Teacher teacher;
Student(String name, Teacher teacher) {
this.name = name;
this.teacher = teacher;
teacher.students.add(this);
}
}
public static void main(String[] args)
throws IOException, ClassNotFoundException {
Teacher t = new Teacher("Ms. Anamaneni");
new Student("Navika", t);
new Student("Srihitha", t);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
try (ObjectOutputStream out = new ObjectOutputStream(bytes)) {
out.writeObject(t);
}
try (ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(bytes.toByteArray()))) {
Teacher copy = (Teacher) in.readObject();
Student first = copy.students.get(0);
Student second = copy.students.get(1);
System.out.println(copy.name + " teaches " + copy.students.size());
System.out.println("Shared teacher kept? "
+ (first.teacher == second.teacher));
System.out.println("Cycle restored? " + (first.teacher == copy));
}
}
}Ms. Anamaneni teaches 2
Shared teacher kept? true
Cycle restored? trueExplanation: The teacher refers to two students, and each student refers back to the teacher, a cycle. Serialization wrote the teacher once; each later reference to it became a back-reference. After reading, both students point to the same restored teacher, which is the object that was read.
Common Mistakes
A non-serializable superclass without a no-argument constructor. Deserialization fails with InvalidClassException: no valid constructor.
Expecting superclass fields to survive when the superclass isn't serializable. They're reset by its constructor.
Writing a changed object again on the same stream and expecting the new values. Use reset().
Practice
- Easy: Make
Personin the first example serializable and confirm that "City High" now survives. - Harder: Remove the no-argument constructor from
Person(add one with a parameter instead) and observe the exception when reading.
Key Takeaways
Was this tutorial helpful?