Custom Serialization: writeObject and readObject
Override writeObject and readObject for encryption, compression, or custom field handling.
Custom Serialization: writeObject and readObject is a free Java Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
When Default Serialization Is Not Enough
Default serialization saves all non-transient fields as-is. Sometimes you need encryption, compression, or a different representation. That's where writeObject / readObject hooks come in.
Declaring writeObject
Add a private void writeObject(ObjectOutputStream oos) method. Java calls it instead of the default mechanism. You control exactly what gets written.
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); // write non-transient fields normally
oos.writeUTF(encrypt(this.secret)); // write encrypted secret
}Declaring readObject
Add a private void readObject(ObjectInputStream ois) method. Java calls it during deserialization. You read in the exact same order you wrote.
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject(); // restore non-transient fields
this.secret = decrypt(ois.readUTF()); // decrypt and restore
}Order Matters
Whatever you write in writeObject must be read back in the same order in readObject. Mismatched order causes StreamCorruptedException.
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.writeInt(version);
oos.writeUTF(data);
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
this.version = ois.readInt(); // same order
this.data = ois.readUTF();
}Compressing Data During Serialization
Wrap the stream with GZIPOutputStream inside writeObject to compress large fields, then GZIPInputStream inside readObject to decompress.
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (GZIPOutputStream gz = new GZIPOutputStream(bos)) {
gz.write(largePayload.getBytes());
}
oos.writeObject(bos.toByteArray());
}The readResolve Hook
readResolve() is called after deserialization. Return a replacement object — useful for singletons and enums to ensure only one instance exists after deserialization.
public class AppConfig implements Serializable {
private static final AppConfig INSTANCE = new AppConfig();
private AppConfig() {}
public static AppConfig getInstance() { return INSTANCE; }
private Object readResolve() { return INSTANCE; } // return singleton
}The writeReplace Hook
writeReplace() is called before serialization. Return a different object to be serialized instead — useful for proxy patterns and serialization surrogates.
private Object writeReplace() {
return new UserProxy(this.id); // serialize a lightweight proxy
}Validating Fields in readObject
You can add validation logic inside readObject to reject invalid or tampered data as part of a defense-in-depth strategy.
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject();
if (age < 0 || age > 150) {
throw new InvalidObjectException("Invalid age: " + age);
}
}Handling Version Changes
Use a version field in writeObject so that readObject can handle both old and new formats gracefully during migrations.
private static final int SERIAL_VERSION = 2;
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeInt(SERIAL_VERSION);
if (SERIAL_VERSION >= 2) oos.writeUTF(email); // new field
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject();
int v = ois.readInt();
if (v >= 2) this.email = ois.readUTF();
}Serialization Proxy Pattern
A robust approach: write a private static inner class that captures the minimum state, then use writeReplace to write the proxy and its readResolve to reconstruct the original.
Performance Considerations
Custom serialization adds method-call overhead but can reduce payload size significantly. Benchmark before optimizing; use Externalizable for maximum control.
Quick Check
What must be true about the order in writeObject and readObject?
Recap
Override writeObject / readObject for encryption, compression, or custom formats. Use readResolve for singletons. Always read in the same order as you write.
Frequently asked questions
Is the “Custom Serialization: writeObject and readObject” lesson free?
Yes — the full text of “Custom Serialization: writeObject and readObject” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.
What will I learn in “Custom Serialization: writeObject and readObject”?
Override writeObject and readObject for encryption, compression, or custom field handling. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Java Academy?
No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Custom Serialization: writeObject and readObject” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Java Academy lesson?
Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Java Serialization Basics
- transient Fields and serialVersionUID
- Custom Serialization: writeObject and readObject
- Modern Alternatives: JSON and Protocol Buffers