Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b4BL3cEPHQTk2McE7C
copying
Companion code for the ankurm.com post "Deep Copy vs Shallow Copy in Java: clone(), Copy Constructors, Serialization and Records."
Module in java-core-examples, the Java-core series. There is no docs/ folder: the deeper material lives in the post itself, inside its expandable sections.
Versions this was built and tested against
| Component | Version | Notes |
|---|---|---|
| JDK | 25.0.5+8 (Temurin, LTS) | Records, List.copyOf, Arrays.copyOf. |
| JUnit Jupiter | 5.11.0 | Same as the sibling modules. |
| Maven | 3.9.11 |
Quickstart
export JAVA_HOME=/path/to/jdk-25
mvn compile
scripts/run.sh CloneDemo # one demo
scripts/broken.sh # the three deliberate compile errors
scripts/run-all.sh # regenerate every file in output/
What's in here
| File | What it shows |
|---|---|
ShallowVsDeepDemo.java |
Assignment vs shallow copy vs deep copy on one Person graph. |
CloneDemo.java |
clone() skips the constructor, is shallow, needs Cloneable, and a parent that uses new breaks subclasses. Arrays and ArrayList.clone() are shallow too. |
src/broken/FinalFieldClone.java |
Does not compile: a final field cannot be deep-copied inside clone(). Not part of the Maven build. |
src/broken/ForgotToOverride.java |
Does not compile: implements Cloneable without a public clone() override leaves clone() protected. |
src/broken/WithExpression.java |
Does not compile on JDK 25: the with expression of JEP 468 (derived record creation) is not part of the language yet. |
CopyConstructorDemo.java |
Copy constructors with final fields, subtype slicing, and a virtual copy() that fixes it. |
SerializationCopyDemo.java |
Deep copy via ObjectOutputStream: transient loss, shared references and cycles preserved, NotSerializableException. |
RecordCopyDemo.java |
Records are only shallowly immutable: List.copyOf in the compact constructor, withers, a mutable Date component. |
DefensiveCopyDemo.java |
Leaks through constructor and getter; unmodifiableList (view) vs List.copyOf (snapshot). |
CollectionCopyDemo.java |
Every standard list, map and array copy is shallow; the deep versions. |
CopyOfIdentityDemo.java |
When List.copyOf returns the same instance and when it copies; null elements. |
GraphCopyDemo.java |
Shared nodes and cycles: a recursive copy constructor duplicates the shared node and overflows the stack on a cycle; an identity-map copy and serialization both handle them. |
SerializationRulesDemo.java |
Which constructors run during a serialization copy (classes vs records) and an ObjectInputFilter allow-list. |
RecordArrayDemo.java |
An array record component leaks through the constructor and the accessor; the generated equals() compares arrays by identity. |
DefensiveCloneDemo.java |
Why a defensive copy must not call clone() on a non-final parameter type (a Date subclass hands back itself). |
CopyCostDemo.java |
Indicative cost of copy constructor vs clone() vs serialization. Timing: results drift per run. |
CopyingTest.java, CopyingExtrasTest.java |
Pin every claim above as an assertion (17 + 7 = 24 tests). |
output/01-17 |
Captured runs of everything above. 10-cost.txt is the only non-deterministic file. |
License
MIT - see LICENSE.