Java enums implicitly implement Comparable and Serializable. What ordering do they use, and what is special about enum serialization?
answer
- Natural order = ordinal = declaration order; compareTo is final
- Need other order -> use a Comparator
- Never persist ordinal(); store name()
- Serialization writes name only; valueOf resolves existing constant
- Custom serialization hooks ignored; identity (==) preserved
basics
~20 sEnums sort by declaration order (their ordinal), and compareTo is final so you can't change it. They serialize by name, not by field state, so deserialization always returns the existing constant — identity is preserved.
solid answer
~40 sEvery enum implicitly implements Comparable, ordering constants by their ordinal — the zero-based position in declaration order — and Enum.compareTo is final, so you cannot override the natural ordering. If you need a different order, sort with an explicit Comparator instead. Enums also implicitly implement Serializable, but with a special, constant-only protocol: only the constant's name() is written, and on deserialization valueOf(name) is used to resolve the existing singleton constant. Fields are NOT serialized, custom writeObject/readObject/serialPersistentFields are ignored, and there's no serialVersionUID concern. This guarantees that a deserialized constant is == the in-VM constant, preserving the singleton identity that makes switch and == on enums valid. The catch: reordering or renaming constants changes ordinal-based ordering and name-based serialization compatibility, so never persist ordinal() and be careful renaming constants used in serialized streams.
code
java · 12 linesenum Size { SMALL, MEDIUM, LARGE } // ordinals 0,1,2
// natural ordering = declaration order
List<Size> xs = new ArrayList<>(List.of(Size.LARGE, Size.SMALL));
Collections.sort(xs); // [SMALL, LARGE]
// custom order needs a Comparator (compareTo is final):
xs.sort(Comparator.comparing(Size::name)); // alphabetical
// persist the stable name, not the ordinal:
String stored = Size.LARGE.name(); // "LARGE"
Size back = Size.valueOf(stored); // back == Size.LARGE (same constant)go deeper
Knows enums can be compared and that they sort in the order written; knows valueOf turns a name back into a constant.
Explains ordinal-based natural ordering, that compareTo is final, and to store name() not ordinal().
Explains the name-only serialization protocol, that custom hooks are ignored, and that identity/== is preserved, plus rename/reorder compatibility hazards.
Designs persistence and wire formats around stable names, reasons about schema evolution of enum sets, and leverages serialization identity for the bulletproof enum singleton.
## Two interfaces you never declare Every `enum` automatically implements **`Comparable`** and **`java.io.Serializable`**. You don't write them, and in fact you can't change how they behave in the usual ways. ## Comparable: ordering by ordinal Each constant has an **`ordinal()`** — its zero-based index in **declaration order**. `Enum.compareTo` compares ordinals, so the *natural ordering* of an enum is simply the order you wrote the constants in: ```java enum Size { SMALL, MEDIUM, LARGE } // ordinals 0, 1, 2 Size.SMALL.compareTo(Size.LARGE); // negative (0 - 2) ``` Key facts: - `compareTo` is **`final`** on `java.lang.Enum` — you **cannot override** the natural ordering. If you want a different order (e.g. alphabetical, or by a domain field), build an explicit `Comparator` (e.g. `Comparator.comparing(Size::displayName)`) and pass it to the sort/collection. - Natural ordering is **consistent with `equals`** (each constant is unique), so enums are safe keys in `TreeSet`/`TreeMap` out of the box. - **Don't persist `ordinal()`** in a database or file. Ordinals shift if you insert or reorder constants, silently corrupting stored data. Store `name()` (a stable String) instead, and convert back with `valueOf`. ## Serializable: a special name-based protocol Normal Java serialization writes an object's fields. **Enums are different** — the JLS/serialization spec defines a dedicated form: - The serialized form of an enum constant is **only its `name()`** (plus the enum type). No instance fields are written, even custom ones. - On deserialization, the runtime calls **`valueOf(EnumType.class, name)`**, which returns the **existing constant** in the current VM. No new object is created. - Custom serialization hooks — `writeObject`, `readObject`, `readObjectNoData`, `writeReplace`, `readResolve`, `serialPersistentFields` — are **ignored** for enums. You cannot customize the form. - There is **no `serialVersionUID`** to manage. ### Why this matters: identity is preserved Because deserialization resolves to the existing constant, a deserialized `Size.LARGE` is **`==`** the in-VM `Size.LARGE`. This is what keeps `switch (size)` and `size == Size.LARGE` correct even across serialization boundaries, and it's the reason the **enum singleton pattern** is bulletproof against serialization attacks (you can't manufacture a second instance via the stream). ### The compatibility caveat Since the wire form is the **name**, **renaming a constant breaks** deserialization of older streams (the old name won't `valueOf`), and **adding** a constant is forward-compatible only if old VMs never need the new name. Removing a constant breaks any stream that referenced it. So: add freely at the end if you don't persist ordinals; rename/remove with care. ## Putting it together Use the natural (declaration) order when it is meaningful and stable; otherwise sort with a `Comparator`. Persist `name()`, never `ordinal()`. Rely on name-based serialization to keep constant identity — and lean on it deliberately when using an enum as a robust singleton.
- Why is persisting ordinal() dangerous?Ordinals are positional. Inserting or reordering constants shifts them, so previously stored ordinals now map to the wrong constants and silently corrupt data. Persist name() instead.
- How does name-based serialization make the enum singleton attack-proof?Deserialization calls valueOf(name) and returns the existing constant rather than constructing a new object, and custom readResolve isn't even needed. So an attacker can't fabricate a second instance through the serialization stream.
Enum serialization is like sending a contact's name rather than a photocopy of their whole file: the receiver looks the name up in their own address book and gets the one true person, never a duplicate.
saying these in an interview costs you the question
- Overriding compareTo on an enum — it's final and won't compile.
- Storing ordinal() in a DB/file as the stable id.
- Believing enum serialization writes instance fields or honors writeObject/readResolve — it doesn't.
- Assuming a deserialized constant is a new object — it's the same == instance.