How does Externalizable differ from implementing Serializable with writeObject/readObject?
answer
- Externalizable = full manual control, writeExternal/readExternal, public
- No automatic field serialization at all — you write everything
- Deserialization calls the PUBLIC no-arg constructor, THEN readExternal
- Missing no-arg constructor → InvalidClassException
- Serializable hooks are private/per-class; Externalizable methods are public/inherited
basics
~20 sExternalizable is a stronger interface where YOU write all the serialization code by hand in writeExternal/readExternal — there is no automatic field handling at all. Serializable with writeObject/readObject still does the default work for you unless you opt out.
solid answer
~50 sExternalizable extends Serializable and gives you total manual control. You implement two public methods, writeExternal(ObjectOutput) and readExternal(ObjectInput), and the runtime does NO automatic field serialization — you must write and read every piece of state yourself. Critically, on deserialization the runtime calls your class's PUBLIC no-arg constructor, then invokes readExternal to populate the object; this is unlike Serializable, where no constructor runs. So an Externalizable class must have a public no-arg constructor, and forgetting it causes an InvalidClassException at runtime. The upside is performance and full control over the wire format; the downside is fragility — you own versioning entirely, there's no defaultReadObject safety net, and the public methods plus public constructor weaken encapsulation. In practice Serializable with writeObject/readObject is preferred for most classes; Externalizable is reserved for hot paths where the default format's overhead matters.
go deeper
Knows Externalizable exists and means doing serialization 'by hand'.
Can implement writeExternal/readExternal and knows no automatic field handling happens.
Explains the public no-arg constructor requirement and the constructor-runs-vs-not contrast with Serializable, plus the performance/encapsulation trade-offs.
Weighs Externalizable against Serializable hooks and against abandoning Java serialization (Protobuf/JSON) for new systems; reasons about versioning ownership, attack surface, and inheritance pitfalls.
## Two interfaces, two philosophies Java offers two opt-in mechanisms to make an object streamable: - **`Serializable`** — a *marker* interface (no methods). The runtime does the work automatically by reflecting over your fields. You can optionally customize with the private `writeObject`/`readObject` hooks, but even then `defaultWriteObject`/`defaultReadObject` are available to fall back on the automatic behavior. - **`Externalizable`** — extends `Serializable` but declares two PUBLIC methods you must implement: ```java public void writeExternal(ObjectOutput out) throws IOException; public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; ``` With `Externalizable` the runtime does **zero** automatic field handling. Whatever you don't write in `writeExternal` is simply not saved; whatever you don't read in `readExternal` stays at its default (zero/null). You own the entire wire format. ## The constructor difference — the key exam point This is the distinction interviewers probe: - **Serializable path:** during deserialization the object is created WITHOUT calling any constructor of the serializable class. The runtime uses a special allocation (`ObjectStreamClass` / `Unsafe`) and then fills fields. That's why `readObject` is where you must re-validate invariants. - **Externalizable path:** during deserialization the runtime **calls the public no-arg constructor first**, producing a default object, and THEN calls `readExternal(in)` to populate it. Consequences: 1. An `Externalizable` class **must** have an accessible (public) no-arg constructor. If it doesn't, you get `java.io.InvalidClassException: ...; no valid constructor` at deserialization time. 2. Because a real constructor runs, an `Externalizable` object briefly exists in a default state before `readExternal` fills it — keep that constructor cheap and side-effect-free. ## Visibility and encapsulation `writeExternal`/`readExternal` are **public** (they're interface methods). That means any caller can invoke them and mutate or re-emit your object's state — a real encapsulation leak. Subclasses also inherit them, so a subclass must call `super.writeExternal`/`super.readExternal` and add its own fields, which is easy to get wrong. By contrast `writeObject`/`readObject` are **private** and per-class, so the hierarchy composes cleanly. ## Performance and control Default `Serializable` writes field names and type metadata and is relatively verbose; `Externalizable` lets you write a compact, hand-tuned format (e.g. just the raw ints in order). Historically this was a meaningful speedup for high-volume objects. So the trade is **speed/compactness vs. safety/maintainability**. ## Versioning Both use `serialVersionUID` to detect class-version mismatches. But `Serializable` gives you `GetField`/`PutField` and the `defaultReadObject` fallback to evolve schemas gracefully; with `Externalizable` you must hand-roll version tags in your byte format and branch on them. More power, more rope. ## When to choose which - Default to `Serializable` (+ `writeObject`/`readObject` if you need tweaks). It's safer, composes through inheritance, and has fallbacks. - Reach for `Externalizable` only when profiling shows the default format is a bottleneck and you can own the format and constructor discipline. - For greenfield cross-system data, many teams avoid Java serialization entirely (JSON, Protobuf, Avro) because of its security and versioning hazards — but that's a separate decision.
- What exception do you get if an Externalizable class lacks a public no-arg constructor, and when?java.io.InvalidClassException (message like 'no valid constructor'), thrown at deserialization time when the runtime tries to instantiate the object before calling readExternal.
- Why might Externalizable be considered a security/encapsulation downgrade versus the writeObject hooks?Its writeExternal/readExternal are public interface methods, so external code can call them to read or overwrite internal state, and subclasses inherit them. The private, per-class writeObject/readObject keep state access encapsulated and composable across the hierarchy.
saying these in an interview costs you the question
- Saying Externalizable still does default field serialization (it does none)
- Claiming Serializable invokes the no-arg constructor (only Externalizable does)
- Forgetting that Externalizable requires a public no-arg constructor
- Thinking writeExternal/readExternal are private like writeObject/readObject