Student.toString() stores text, not a reconstructable object. The first program writes Student{id=42, name='Mira', cgpa=8.6, address=Pune-411001, password=gate@123} to student.ser with ObjectOutputStream, then restores it with ObjectInputStream. Its output separates retained object-graph state from transient and static state.
Build the correct mental model for Java Serialization
Serialization converts the state of a Java object graph into a byte stream. Deserialization uses that stream to build a new object graph. Serializable is a marker contract checked when ObjectOutputStream.writeObject() reaches a class. The model object does not call a Serializable method.
The mechanism is separate from the destination. Bytes can go to a file, memory buffer or connection. The student.ser file is a saved artefact; Java does not guarantee that it is human-readable or has a fixed size.
Java serialization suits controlled Java-to-Java persistence or transfer. Use a database, JSON or another explicit format when data must be queried, exchanged across languages or evolve independently. The Coding & Skill Development Courses category connects this mechanism to related programming skills.
Write and read one complete object
Save this complete file as SerializationDemo.java. Both Student and its reachable Address implement the marker interface.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
final class Address implements Serializable {
private static final long serialVersionUID = 1L;
private final String city;
private final int pinCode;
Address(String city, int pinCode) {
this.city = city;
this.pinCode = pinCode;
}
@Override
public String toString() {
return city + "-" + pinCode;
}
}
final class Student implements Serializable {
private static final long serialVersionUID = 1L;
private final int id;
private final String name;
private final double cgpa;
private final Address address;
private final transient String password;
Student(int id, String name, double cgpa, Address address, String password) {
this.id = id;
this.name = name;
this.cgpa = cgpa;
this.address = address;
this.password = password;
}
@Override
public String toString() {
return "Student{id=" + id + ", name='" + name + "', cgpa=" + cgpa
+ ", address=" + address + ", password=" + password + "}";
}
}
public class SerializationDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student original = new Student(42, "Mira", 8.6,
new Address("Pune", 411001), "gate@123");
File file = new File("student.ser");
System.out.println("Before: " + original);
try (ObjectOutputStream output =
new ObjectOutputStream(new FileOutputStream(file))) {
output.writeObject(original);
}
System.out.println("Saved to: " + file.getName());
Student restored;
try (ObjectInputStream input =
new ObjectInputStream(new FileInputStream(file))) {
restored = (Student) input.readObject();
}
System.out.println("After: " + restored);
System.out.println("Same object: " + (original == restored));
}
}Compile and run it:
javac SerializationDemo.java
java SerializationDemoThe output is exactly:
Before: Student{id=42, name='Mira', cgpa=8.6, address=Pune-411001, password=gate@123}
Saved to: student.ser
After: Student{id=42, name='Mira', cgpa=8.6, address=Pune-411001, password=null}
Same object: falseJava restores 42, 8.6, "Mira", "Pune", 411001 and the reachable Address. It gives the excluded reference its default value, null. Deserialization creates a distinct Student, so identity comparison is false even though the retained values match.

Follow the reachable object graph and its exclusions
The Student -> Address link shows the transitive rule. Every non-transient object reachable from the root must be serializable. Remove implements Serializable only from Address and compilation still succeeds, but writeObject(original) throws NotSerializableException naming Address. Either make Address serializable because it is retained state, or mark the field transient because exclusion is deliberate.
After reading, ordinary instance fields retain id=42, name="Mira", cgpa=8.6 and address=Pune-411001. The transient reference password becomes null. A hypothetical static String college = "KG" comes from the currently loaded class, not student.ser. A transient primitive such as int attempts = 3 becomes its default value, 0, unless custom restoration assigns something else.
Java preserves shared references within one stream. Compile the first file, then save this second program as SharedReferenceDemo.java:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SharedReferenceDemo {
public static void main(String[] args) throws Exception {
Address shared = new Address("Pune", 411001);
Address[] original = {shared, shared};
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
try (ObjectOutputStream output = new ObjectOutputStream(bytes)) {
output.writeObject(original);
}
Address[] restored;
try (ObjectInputStream input = new ObjectInputStream(
new ByteArrayInputStream(bytes.toByteArray()))) {
restored = (Address[]) input.readObject();
}
System.out.println(restored[0] == restored[1]);
}
}Compile and run it with javac SharedReferenceDemo.java && java SharedReferenceDemo. It prints true: both array slots point to one restored Address, because the stream records a back-reference. Binary Trees and Binary Search Trees offers a contrasting graph structure.
Control class evolution with serialVersionUID
Both classes declare private static final long serialVersionUID = 1L. The stream records class identity and version information. An explicit UID makes the compatibility decision visible instead of depending on a generated value that may change after class edits.
Try two runs. First, compile version 1 with UID 1L, create student.ser, and keep the file. Next, add private String branch; to Student, retain UID 1L, and use a read-only main on the old file. The old stream has no branch value, so restored.branch is null. The serializable Student constructor and field initializer do not run to invent a value during reading.
Now change only the Student UID to 2L and read the same file. Java throws InvalidClassException; its full message can vary by JVM. Keeping a UID unchanged does not make every structural change compatible, and it should never disguise two meaningfully different schemas as safe.

Diagnose common failures and protect the boundary
Use the symptom to find the faulty assumption:
NotSerializableException: a reachable, non-transient object such asAddresslacks the contract. Make it serializable or exclude it deliberately.InvalidClassException: the stream version is incompatible with the loaded class. Handle the schema intentionally instead of changing the UID until the error disappears.EOFException: the reader commonly calledreadObject()more times than the writer calledwriteObject(). Align the count and order.StreamCorruptedException: the input is not a valid object stream, or extra stream headers were appended incorrectly. Recreate the file or use a format designed for appending.
Two outputs are not failures. password=null is the intended result of transient, and original == restored is false because Java built a new graph. Compare fields or implement a suitable equals() when you need value equality.
Never deserialize bytes from an untrusted user, attachment or network source merely because the cast compiles. Keep this example local and controlled. transient does not encrypt or erase a secret. It only excludes the field from the default serialized form, while student.ser still needs normal access controls.
Practise output and interview question shapes
Useful assessment prompts ask you to predict values after a write-read cycle, find the first non-serializable reachable field, separate instance, transient and static state, explain a UID mismatch, or reason about constructors.
KnowledgeGate currently has about 50 Java practice questions across its Java topics.
Try these before opening the answers:
E1: Change the password field to
transient int attempts = 3. What value follows a default read?E2: Add
static String college = "KG", write the object, setStudent.college = "AI"in the same JVM, then read. What doesrestored.collegeobserve?E3: Point
homeandmailingat the sameAddress("Pune", 411001)in one object. Isrestored.home == restored.mailingtrue or false?E4: Remove
SerializablefromAddress. Where does failure occur?
A1:
0, the defaultintvalue, because the field is transient.A2:
"AI", because static state belongs to the loaded class and is not restored from the stream.A3:
true, because the shared reference is preserved within the serialized graph.A4: Compilation succeeds, then
writeObject()throwsNotSerializableExceptionfor the reachableAddressobject.
Short version and the next Java step
Implement Serializable on each retained referenced-object class. Write with ObjectOutputStream, read in matching order with ObjectInputStream, exclude fields with transient, and manage evolution with an explicit serialVersionUID. Run both programs and predict their output. In the versioning experiment, a newly written object restores branch="CSE"; the old stream restores null.
For the full language sequence, use the Java Course: Concepts, MCQs & Coding Questions. To apply Java in interview data structures, continue with the DSA using Java course.




