skip to content

If a Serializable class extends a non-serializable superclass, what extra requirement must the superclass meet, and why?

level: middleimportance: must knowfreq 55%

answer

  1. Parent not Serializable -> needs accessible no-arg constructor
  2. Parent's no-arg constructor RUNS during deserialization
  3. Parent fields are NOT in the stream -> not restored
  4. Missing/private no-arg ctor -> InvalidClassException at read
  5. Opposite of serializable parent (whose ctor is skipped)

basics

~20 s

The non-serializable parent must have an accessible no-argument constructor. During deserialization Java does not read the parent's fields from the stream; instead it calls that no-arg constructor to set up the parent part of the object.

solid answer

~40 s

A class can be Serializable even if its parent is not. But deserialization treats the two parts differently. The serializable subclass's fields are written to and read back from the stream. The non-serializable superclass's state is NOT in the stream at all. So to rebuild the parent portion, Java invokes the superclass's no-argument (no-arg) constructor during deserialization. Therefore that no-arg constructor must exist and be accessible to the subclass (public, protected, or package-private in the same package). If it is missing, or only private/parameterized constructors exist, deserialization throws InvalidClassException (often wrapping NotSerializableException semantics). Note the constructor actually runs, unlike for serializable superclasses whose constructors are skipped, which is a common surprise.

go deeper

for a junior

Knows a class can be Serializable without its parent being Serializable, and that the parent then needs a no-argument constructor.

for a middle

Explains that the parent's fields aren't in the stream and the parent's no-arg constructor is invoked during deserialization to rebuild that part; names InvalidClassException for a missing/inaccessible constructor.

for a senior

Contrasts serializable vs non-serializable superclass constructor behavior, reasons about lost parent state and constructor side effects, and knows how to preserve parent fields via custom writeObject/readObject.

for a principal

Weighs serialization-with-inheritance against design risks (constructor side effects firing on deserialize, security of running ctors, fragile cross-version compatibility) and steers teams toward safer alternatives (DTOs, JSON, explicit factories).

## Setup: what serialization is **Serialization** is converting a live Java object (its fields, recursively) into a byte stream so it can be saved to disk or sent over a network. **Deserialization** is the reverse: reconstructing the object from those bytes. In core Java this is done with `ObjectOutputStream.writeObject(obj)` and `ObjectInputStream.readObject()`. A class opts in by implementing the **marker interface** `java.io.Serializable` (an interface with no methods; it just flags the class as serializable). ## The scenario Consider a serializable class whose **superclass** (parent class it `extends`) is NOT serializable: ``` class Animal { // NOT Serializable String species; } class Dog extends Animal implements Serializable { // IS Serializable String name; } ``` This is **allowed** and works — but with an important asymmetry. ## What gets written vs. what gets restored When you serialize a `Dog`, the serialization machinery walks up the class hierarchy and serializes the fields of every class **in the chain that is itself Serializable**. Here only `Dog` is Serializable, so only `Dog`'s field `name` is written. The fields declared in `Animal` (e.g. `species`) are **not** written to the stream — the non-serializable superclass's state is simply not captured. On **deserialization** Java must build a complete object including the `Animal` part. Since the `Animal` fields are not in the stream, Java cannot read them back. Instead, the deserialization process **calls the no-argument constructor of the first non-serializable superclass** (here `Animal()`), and lets the normal constructor chain run for the non-serializable part. That constructor reinitializes the parent's fields to whatever defaults/values the no-arg constructor sets (so `species` will be whatever `Animal()` assigns, NOT the value the object had before serialization). The serializable subclass's fields (`name`) are then filled in from the stream — **without** running `Dog`'s constructor. ## The requirement, precisely Because Java must call that no-arg constructor, the non-serializable superclass **must have an accessible no-arg (no-argument) constructor**. "Accessible" means visible to the subclass: `public`, `protected`, or package-private when in the same package. If the superclass: - declares **no** constructors at all → the compiler supplies an implicit public no-arg constructor → fine. - declares **only** parameterized constructors → there is no no-arg constructor → **fails**. - has a no-arg constructor but it is **private** → not accessible → **fails**. When the requirement is violated, deserialization throws `java.io.InvalidClassException` complaining that the class has no valid (accessible no-arg) constructor. (Serialization, i.e. writing, generally still succeeds; the failure surfaces at read time.) ## The contrast that trips people up For a **Serializable** superclass, its constructor is **NOT** called on deserialization — the object is allocated and its fields are populated directly from the stream, bypassing constructors entirely. For a **non-serializable** superclass it is the opposite: its no-arg constructor **IS** called. So the rule of thumb is: *constructors run for the non-serializable part of the hierarchy and are skipped for the serializable part.* ## Practical consequences 1. Any state held in the non-serializable parent is **lost** across a serialize/deserialize round-trip unless you explicitly preserve it (e.g. by giving the subclass `writeObject`/`readObject` methods that read and re-set the parent's fields). 2. Side effects in the parent's no-arg constructor (logging, ID generation, registering with a registry) **will execute** during deserialization — sometimes unexpectedly. 3. If you control the parent, the cheapest fix to a deserialization failure is to add an accessible no-arg constructor.

  • What exception do you get, and at write or read time, if the non-serializable superclass lacks an accessible no-arg constructor?
    You typically get a java.io.InvalidClassException at deserialization (read) time, stating the class has no valid (accessible no-arg) constructor. Writing usually succeeds.
  • After a round-trip, what value does an inherited (parent-declared) field hold?
    Whatever the parent's no-arg constructor assigns it (or the type default if unassigned) - NOT the value the live object had before serialization, because that state was never written to the stream.

saying these in an interview costs you the question

  • Claiming the superclass must also implement Serializable - it need not
  • Saying the parent's fields are serialized and restored - they are not
  • Saying NO constructors run during deserialization - the non-serializable parent's no-arg ctor does run
  • Thinking a private or parameterized-only constructor is enough

context