Why is it significant that clone() bypasses constructors, and how does that affect final fields and invariants?
answer
- clone() = allocate + field-copy, NO constructor
- Skipped constructor → invariants not re-checked
- final fields can't be reassigned post-clone → clash with immutability
- Inherited clone silently shallow-copies new subclass fields
- Copy constructor runs validation + keeps fields final
basics
~20 sclone() builds a new object by copying fields directly in the JVM, without running any constructor. So validation and setup in your constructor never run, and you cannot reassign final fields to fresh deep copies — both make clone() risky and hard to get right.
solid answer
~50 sObject.clone() creates the new instance the way deserialization and reflection-based allocation do: it allocates the object and copies fields bit-by-bit without invoking any constructor. The consequences are significant. First, any invariant-enforcing or normalizing logic in your constructor is skipped, so a clone can end up in a state the constructor would have rejected — you must re-establish invariants by hand in clone(). Second, because the copy already exists when clone() returns, you can only mutate non-final fields afterward; a final field keeps the shallow-copied reference, so you cannot point it at a deep copy. This is the core conflict between clone() and immutability. Third, subclasses inherit a clone() they may not be prepared for. These constructor-bypass problems — plus the broken Cloneable contract — are why Effective Java recommends copy constructors or copy factories, which run normal construction, can deep-copy into final fields, and enforce invariants naturally.
go deeper
Aware that clone() copies fields and does not call your constructor; can repeat that final fields are 'a problem' with clone.
Explains that skipped constructors mean validation is bypassed and that final fields cannot be reassigned for a deep copy; can pick a copy constructor when those matter.
Connects constructor-bypass to invariants, immutability, and inheritance fragility, and argues convincingly for copy constructors/factories with concrete examples.
Treats constructor-bypass as one of a family of extralinguistic instantiation paths (clone, deserialization, reflection) and sets defenses across them; bans clone in style guides except where the platform forces it.
## How clone() creates the object Normally a Java object comes into existence through a **constructor**: `new Foo(...)` allocates memory and then runs the constructor body, which assigns fields, validates arguments, and establishes the class's **invariants** (the always-true facts about a valid object, e.g. 'balance ≥ 0' or 'the list is never null'). `Object.clone()` does **not** do this. Like deserialization and reflective allocation (`Unsafe.allocateInstance`), it asks the JVM to allocate a fresh object of the right runtime class and copy each field's value from the source — **no constructor runs**. This 'extralinguistic' object creation is the root of clone()'s problems. ## Consequence 1 — invariants are not re-checked If your constructor validates or normalizes input: ```java class Percentage { private final int value; // invariant: 0..100 Percentage(int v) { if (v < 0 || v > 100) throw new IllegalArgumentException(); this.value = v; } } ``` that check never runs during `clone()`. The clone simply inherits whatever the source held. Usually that is fine (the source was valid), but if `clone()` needs to *adjust* state — for instance to deep-copy a field and the deep copy must satisfy some relationship — none of the constructor's guard logic helps you. You must re-establish every invariant by hand inside `clone()`. ## Consequence 2 — the final-field conflict Deep cloning works by **reassigning** reference fields *after* `super.clone()` returns: ```java copy.list = new ArrayList<>(this.list); ``` But a `final` field can be assigned **only once**, at construction. After `super.clone()` the object already exists, so its final fields are already set (to the *shallow* copy of the source's references). You cannot repoint a final field at a deep copy. Therefore a class that is both **immutable** (all-final fields, the recommended design) and wants a *deep* `clone()` is essentially impossible to write correctly. Cloning fundamentally fights immutability. ## Consequence 3 — fragile across inheritance Because `clone()` is inherited, a subclass automatically gets the superclass's clone behavior even if the subclass adds new mutable fields that need deep copying. If the author forgets to override `clone()` in the subclass, those new fields are silently shallow-copied. The `Cloneable` machinery gives no compile-time help here. ## The recommended alternative A **copy constructor** (`Foo(Foo other)`) or **copy factory** (`static Foo copyOf(Foo other)`) sidesteps all three problems: - It is an ordinary constructor, so it **runs validation** and can normalize. - It can pass **deep-copied** values straight in, so fields stay **final**. - It does not rely on the broken `Cloneable` contract, throws no checked `CloneNotSupportedException`, and can even accept an *interface* type (a 'conversion constructor', e.g. `new ArrayList<>(someCollection)`), which `clone()` cannot. ```java final class Order { private final List<Item> items; Order(List<Item> items) { this.items = List.copyOf(items); } // validates/deep-copies Order(Order other) { this(other.items); } // copy constructor, fields stay final } ``` ## Takeaway `clone()` bypassing the constructor is not a trivia point — it is *why* clone is dangerous: skipped invariants, an irreconcilable clash with `final`/immutability, and inheritance fragility. Prefer copy constructors/factories; reserve `clone()` for places the platform forces it (e.g. arrays, or APIs that demand a `Cloneable`).
- What other Java mechanisms also create objects without invoking a constructor?Deserialization (readObject/ObjectInputStream) and reflective allocation via sun.misc.Unsafe/jdk.internal allocateInstance. Like clone(), they can produce objects in states a constructor would reject, which is why immutable and validated classes must guard these paths (e.g. readResolve/validation in readObject).
- Can a copy constructor accept an interface type where clone() cannot?Yes. A 'conversion constructor' such as new ArrayList<>(Collection) or new TreeSet<>(SortedSet) takes an interface and produces a chosen implementation. clone() always returns the same runtime class as the source, so it cannot convert between implementations.
saying these in an interview costs you the question
- Assuming clone() runs the no-arg or any constructor
- Trying to deep-copy into a final field inside clone()
- Believing a parent's clone() automatically handles a subclass's new fields
- Treating constructor-bypass as harmless trivia rather than the central reason clone is discouraged