Serialization in Java
Serializable, transient, serialVersionUID, and deserialization security risks.
Freeze-drying objects
**ObjectOutputStream.writeObject turns an object — and everything it references — into bytes; ObjectInputStream.readObject rebuilds it. A class opts in by implementing Serializable, a marker interface** with no methods.
transient: skip this field
Fields marked **transient are not saved. On deserialization they come back as defaults** (null, 0, false), because Java doesn't run the class's constructor or field initializers when rebuilding the object.
class Session implements Serializable {
String user;
transient Socket conn; // not saved
}What survives the trip?
copy() serializes and deserializes in memory. What prints?
class Player implements Serializable {
String name = "neo";
transient int score = 99;
}
// copy() = serialize, then deserialize
Player p = copy(new Player());
System.out.println(p.name + " " + p.score);neo 99neo 0null 0
Show the answer
neo 0 — name was saved, but transient score wasn't, and the initializer = 99 doesn't run on deserialization, so it's the default 0.
serialVersionUID: the version label
Without an explicit **serialVersionUID, Java computes one from the class structure, so adding a field makes old saved data fail with InvalidClassException**. Declare it yourself, and compatible changes still load (new fields get defaults).
class Session implements Serializable {
private static final long
serialVersionUID = 1L;
}Compiles, then fails
writeObject accepts any Object, so this compiles — but Point isn't Serializable, so it throws **NotSerializableException** at runtime. The same happens if *any field's* object isn't serializable.
class Point { int x = 1; } // no Serializable
out.writeObject(new Point());
// NotSerializableExceptionThe security problem
Deserialization instantiates classes chosen by the sender and runs their readObject logic. Crafted bytes can chain library classes (gadget chains) into remote code execution. Never deserialize untrusted data — use JSON, or at least an **ObjectInputFilter** allow-list (Java 9).
Lessons from incidents
In 2015 researchers showed gadget chains in a common library (Apache Commons Collections) that enabled remote code execution in major Java servers that accepted serialized objects. Since then, teams use JSON or Protobuf for anything crossing a network boundary.
Key takeaways
- Implement Serializable, or writeObject throws NotSerializableException
- transient fields aren't saved; they come back as defaults
- Declare serialVersionUID to control version compatibility
- Never deserialize untrusted bytes without an ObjectInputFilter
Java's chief language architect Brian Goetz has called serialization a "horrible mistake" from 1997, and OpenJDK has published design notes on better replacements.
Practice questions
What does this print?
class User implements Serializable {
String name = "ana";
transient String pw = "secret";
}
// copy() = serialize, then deserialize
User u = copy(new User());
System.out.println(u.name + " " + u.pw);- ana secret
- ana null
- null null
- Throws NotSerializableException
Check your answer
ana null. transient fields aren't written. On deserialization the class's constructors and field initializers don't run, so pw is left as null.
What does this print?
class Point { int x = 1; }
// copy() = serialize, then deserialize
Point p = copy(new Point());
System.out.println(p.x);- 1
- 0
- Throws NotSerializableException
- Compile error
Check your answer
Throws NotSerializableException. Point doesn't implement Serializable, so writeObject refuses it at runtime. It compiles because writeObject accepts any Object.