Unravel โบ Java concepts โบ I/O and files
Java serialization
Serialization turns an object graph into bytes (ObjectOutputStream) and back (ObjectInputStream).
Learn these first
At a glance
Flat-pack furniture: the object is taken apart into a box of parts with an instruction sheet (the class version).
- 1A class opts in with implements Serializable (a marker interface with no methods)All non-transient, non-static fields are written, recursively, so every referenced object must be serializable too (NotSerializableException otherwise).
- 2private static final long serialVersionUID = 1L; identifies the class versionIf you change the class and the id differs, reading old data fails with InvalidClassException.
- 3transient marks a field to skip (passwords, caches, threads); it comes back as null or 0
- 4Custom hooks: writeObject/readObject private methods, readResolve (keep a singleton a singleton) and Externalizable for full control
- Deserializing untrusted bytes can run arbitrary code ("gadget chains"); it has caused countless remote-code-execution vulnerabilities
- Serialization bypasses constructors and ignores invariants, and it freezes your private fields as a public format
- The stream starts with a magic number 0xACED and writes class descriptors (name, id, field list) followed by field values, with back-references for repeated objects, so cyclic graphs work
import java.io.*;public class Session implements Serializable { private static final long serialVersionUID = 1L; private final String user; private transient String password; Session(String user, String password) { this.user = user; this.password = password; } static byte[] save(Session s) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); try (ObjectOutputStream out = new ObjectOutputStream(bytes)) { out.writeObject(s); } return bytes.toByteArray(); } static Session load(byte[] data) throws IOException, ClassNotFoundException { try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data))) { return (Session) in.readObject(); } }}- line 2 uses Serializable, the core of Java serialization
- line 9 uses ObjectOutputStream, the core of Java serialization
- line 13 uses ObjectInputStream, the core of Java serialization
Read the full explanation (about 2 min)
Serialization turns an object graph into bytes (ObjectOutputStream) and back (ObjectInputStream). It is built in and easy, but fragile and a well-known security risk; prefer JSON or Protobuf.
A mental model
Flat-pack furniture: the object is taken apart into a box of parts with an instruction sheet (the class version). Reading it back rebuilds the object, and it only works if the instruction sheet still matches.
How it works
A class opts in with implements Serializable (a marker interface with no methods). All non-transient, non-static fields are written, recursively, so every referenced object must be serializable too (NotSerializableException otherwise).
private static final long serialVersionUID = 1L; identifies the class version. If you change the class and the id differs, reading old data fails with InvalidClassException.
transient marks a field to skip (passwords, caches, threads); it comes back as null or 0.
Custom hooks: writeObject/readObject private methods, readResolve (keep a singleton a singleton) and Externalizable for full control.
Going deeper
Deserializing untrusted bytes can run arbitrary code ("gadget chains"); it has caused countless remote-code-execution vulnerabilities. Never read serialized Java objects from outside your trust boundary, or use an ObjectInputFilter.
Serialization bypasses constructors and ignores invariants, and it freezes your private fields as a public format. HTTP sessions, caches and message queues that use it break on every refactoring; JSON, Avro or Protobuf give a defined, versioned format.
Records can be serialized safely with a canonical constructor check, and enums serialize by name, which makes them the safest singletons.
Under the hood
The stream starts with a magic number 0xACED and writes class descriptors (name, id, field list) followed by field values, with back-references for repeated objects, so cyclic graphs work. Reflection reads the fields, and the first non-serializable superclass needs a no-argument constructor.
Common mistakes
- Deserializing data from the network or a user upload.
- Forgetting
serialVersionUID. - Serializing an object that holds a non-serializable field.
- Persisting sessions or caches in Java format across deployments.
How Unravel recognises it
When you paste code, Unravel looks for these signs of java serialization. Spotting them yourself is a good habit when you read unfamiliar code:
- Types:
SerializableObjectOutputStreamObjectInputStreamExternalizable - Method calls:
writeObject()readObject()readResolve()writeReplace()
Part of a learning path
- I/O, networking and time ยท previous: NIO.2: Path and Files in practice ยท next: ZIP and GZIP compression
Related concepts
Frequently asked questions
What is Java serialization?
Serialization turns an object graph into bytes (ObjectOutputStream) and back (ObjectInputStream). It is built in and easy, but fragile and a well-known security risk; prefer JSON or Protobuf.
What are the most common mistakes with Java serialization?
Deserializing data from the network or a user upload. Forgetting serialVersionUID. Serializing an object that holds a non-serializable field. Persisting sessions or caches in Java format across deployments.
How can I see Java serialization in real code?
Paste your own Java into Unravel and it will point out where java serialization appears, explain how it works at the detail level you choose, and draw the flow of the code around it.