Java Object Serialization
Java object serialization writes a serializable object graph to a byte stream so it can be stored or transferred and later reconstructed. It uses Java-specific mechanisms and should not be treated as a general-purpose, secure data format.
A class can participate in default Java serialization by implementing `java.io.Serializable`, a marker interface. `ObjectOutputStream` writes objects to an underlying stream, and `ObjectInputStream` reads them back. The objects being written, including referenced objects, generally need to be serializable too.
Serialization records the graph of objects, not just one isolated object. It preserves references within the graph, including shared references and cycles. By default, eligible instance fields are included; `static` fields are class-level rather than part of an instance, and `transient` fields are excluded from default serialization.
A serializable class can declare `serialVersionUID`, a version identifier used when checking stream compatibility during deserialization. If the class definition changes incompatibly, reading an older stream can fail. Having a matching identifier does not itself guarantee that changed classes are compatible.
Deserialization reconstructs objects from the stream, so only read data from trusted sources. Untrusted streams can expose applications to security risks. For long-lived storage or communication between different systems, an explicit format such as JSON or Protocol Buffers is often a better fit.
Key points
- Implement `Serializable` to enable default Java object serialization.
- `ObjectOutputStream` writes objects; `ObjectInputStream` reads them.
- Serialization can include referenced objects and preserves object-graph relationships.
- `transient` instance fields are skipped by default; `static` fields are not per-instance state.
- `serialVersionUID` helps detect stream/class version mismatches but does not guarantee compatibility.
- Do not deserialize untrusted data.
Interview questions
What happens to a `transient` field during default serialization?
A transient instance field is omitted from default serialization. After deserialization, it does not contain the original in-memory value, so application code may need to initialize or recompute it. Custom serialization logic can change how a class handles its state.
How do you make an object serializable in Java?
The class implements `java.io.Serializable`, which is a marker interface. Then an `ObjectOutputStream` can write an instance using `writeObject`, provided the objects reached through its serialized fields are also serializable or otherwise handled. A common practice is to declare a `serialVersionUID` explicitly.
What does Java serialize when it writes an object?
Default serialization writes the object's serializable instance state and follows references to other objects in the graph. It tracks references, so shared objects and cycles can be represented rather than repeatedly treated as unrelated objects. Static fields are not instance state, and transient fields are skipped by default.
What is `serialVersionUID` for?
`serialVersionUID` is a version identifier checked when a serialized stream is read against a class definition. If the stream and class have incompatible identifiers, deserialization can fail with `InvalidClassException`. Declaring the value explicitly avoids relying on a value computed from the class definition, but choosing a matching number does not prove that the class changes are compatible.
Why should an application avoid deserializing untrusted data?
Deserialization processes data that can describe object graphs and may trigger class-specific behavior during reconstruction. Maliciously crafted input can therefore create security risks, especially when dangerous classes are available to the application. Prefer a constrained data format and validate input, or apply appropriate deserialization filters when Java serialization is unavoidable.