skip to content

How does serialization traverse an inheritance hierarchy when a superclass is not Serializable?

level: seniorimportance: should knowfreq 40%

answer

  1. Per-class along the hierarchy: only Serializable classes' fields are saved
  2. Non-Serializable superclass fields = effectively transient (skipped on write)
  3. On read, non-Serializable superclass rebuilt via its no-arg constructor (it RUNS)
  4. No accessible no-arg constructor → InvalidClassException at deserialization
  5. Serializable subclass part: no constructor runs, fields come from stream

basics

~20 s

Only 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 s

Serialization 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

for a junior

Knows that a class must implement Serializable to be saved and that some inherited fields may not survive.

for a middle

Explains that non-Serializable superclass fields are skipped and that the subclass still serializes its own fields.

for a senior

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.

for a principal

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)

context