How does serialization traverse an inheritance hierarchy when a superclass is not Serializable?
answer
- Per-class along the hierarchy: only Serializable classes' fields are saved
- Non-Serializable superclass fields = effectively transient (skipped on write)
- On read, non-Serializable superclass rebuilt via its no-arg constructor (it RUNS)
- No accessible no-arg constructor → InvalidClassException at deserialization
- Serializable subclass part: no constructor runs, fields come from stream
basics
~20 sOnly the fields of Serializable classes in the hierarchy are saved. A non-Serializable superclass's fields are skipped on write, and on read that superclass is rebuilt by calling its no-arg constructor. If it has no accessible no-arg constructor, deserialization fails.
solid answer
~50 sSerialization is per-class along the inheritance chain. When a subclass is Serializable but a superclass is not, the runtime serializes only the state declared by Serializable classes; the non-Serializable superclass's fields are NOT written. On deserialization, the subclass portion is restored field-by-field as usual, but the non-Serializable superclass is reconstructed by invoking its no-arg (default) constructor — so that constructor's logic actually runs, and any superclass field not set there reverts to its constructor/default value. The hard requirement is that the non-Serializable superclass must have an ACCESSIBLE no-arg constructor; if it lacks one, you get an InvalidClassException at deserialization. This is also why such superclass fields effectively behave like transient state. The practical takeaways: don't rely on non-Serializable superclass fields surviving a round trip, and ensure the superclass exposes a no-arg constructor reachable from the serializable subclass.
go deeper
Knows that a class must implement Serializable to be saved and that some inherited fields may not survive.
Explains that non-Serializable superclass fields are skipped and that the subclass still serializes its own fields.
States that the non-Serializable superclass is reconstructed via its no-arg constructor (which runs), requires that constructor be accessible, and knows the InvalidClassException failure mode.
Reasons about designing class hierarchies for serializability, when to push Serializable up vs restore fields manually, and the round-trip data-loss/versioning implications across a system.
## Serialization is class-by-class When you serialize an object, the runtime processes the inheritance chain class by class, from the topmost class downward. For EACH class it asks one question: does this class implement `Serializable`? - If **yes**, that class's instance fields participate (its non-`transient`, non-`static` fields are written, or its `writeObject` runs). - If **no**, that class's fields are **completely ignored** — not written to the stream at all. So `Serializable` is not 'all or nothing' for the whole object; it's evaluated at each level. A subclass can be `Serializable` even if its parent is not. ## Writing: the non-serializable parent is skipped Given: ```java class Animal { // NOT Serializable int legs; Animal() { this.legs = 4; } // no-arg constructor Animal(int legs) { this.legs = legs; } } class Dog extends Animal implements Serializable { String name; } ``` When you serialize a `Dog`, only `Dog`'s fields (`name`) are written. `Animal.legs` is NOT in the stream — it's as if the parent's state is `transient`. ## Reading: the parent is rebuilt via its no-arg constructor This is the crucial and frequently-tested rule. To reconstruct the object the runtime must produce a valid `Animal` part before filling in `Dog`'s fields. Since the parent's state wasn't saved, the runtime **calls the non-Serializable superclass's no-arg constructor** to initialize that part. Result for our example: after deserializing a `Dog`, `legs` is whatever `Animal()` sets it to (here `4`), NOT the value the original object had at serialization time. If the original `Dog` had `legs == 3`, that information is lost — it comes back as `4`. Contrast with the Serializable subclass part: for `Dog` itself, NO constructor runs (that's the normal Serializable behavior); its fields are set directly from the stream. So a mixed hierarchy has split behavior: - Non-Serializable ancestors: constructor runs, fields come from constructor/defaults. - Serializable descendants: no constructor runs, fields come from the stream. ## The hard requirement: an accessible no-arg constructor Because the runtime MUST call the superclass's no-arg constructor, that constructor has to exist and be reachable from the serializable subclass (i.e. `public`, `protected`, or package-private in the same package). If the non-Serializable superclass has only argument-taking constructors (and thus no implicit default constructor), deserialization throws: ``` java.io.InvalidClassException: Dog; no valid constructor ``` This happens at deserialization time, not at compile time, which makes it an easy production surprise. ## Why this design? It preserves the contract that a non-Serializable class never has its state silently persisted (it opted out), while still letting subclasses be serializable. Running the parent's no-arg constructor guarantees the inherited part is in a valid, initialized state. ## Practical guidance - If you need a superclass's fields to survive, make the superclass `Serializable`, or capture/restore those fields manually in the subclass's `writeObject`/`readObject`. - Always ensure a non-Serializable superclass that you intend to extend with serializable subclasses has an accessible no-arg constructor. - Treat non-Serializable superclass fields as effectively transient when reasoning about round trips.
- After deserializing a Serializable Dog whose non-Serializable Animal superclass set legs=4 in its no-arg constructor, what is the value of legs even if the original had legs=3?It is 4. The Animal part is not serialized; on read it is rebuilt by Animal's no-arg constructor, which sets legs=4, so the original value of 3 is lost.
- What error occurs if the non-Serializable superclass has no accessible no-arg constructor, and when?java.io.InvalidClassException ('no valid constructor'), thrown at deserialization time, because the runtime cannot initialize the superclass portion without a reachable no-arg constructor.
saying these in an interview costs you the question
- Thinking the whole object must be Serializable top to bottom
- Claiming non-Serializable superclass fields are restored from the stream (they're skipped)
- Forgetting the superclass no-arg constructor requirement → runtime InvalidClassException
- Assuming the superclass constructor does NOT run (for the non-Serializable part it does)