Object copying in Java is the process of creating a new object with the same state as an existing instance. Historically, copying became relevant at the very beginning of Java's development when the need arose to duplicate objects without manually reconstructing them. Java itself does not provide a universal way to copy objects; instead, it utilizes the contract of clone() and the copy constructor pattern.
The clone() method was introduced in the Cloneable interface to provide a standard mechanism for cloning objects; however, its implementation presents numerous nuances and often leads to bugs.
The main problem is the lack of true deep copying "out of the box" and the possibility of unpredictable behavior when performing shallow cloning. The copy constructor allows for both shallow and deep copying but requires explicit implementation.
For safe and correct copying of complex objects, it is preferable to use a copy constructor or factory method, while clone() should be used with caution—only if the object truly fits this contract.
Example code:
// Shallow cloning using clone() public class Address implements Cloneable { String city; public Address(String city) { this.city = city; } @Override public Address clone() throws CloneNotSupportedException { return (Address) super.clone(); } } public class Person implements Cloneable { String name; Address address; public Person(String name, Address address) { this.name = name; this.address = address; } @Override public Person clone() throws CloneNotSupportedException { Person copy = (Person) super.clone(); copy.address = address.clone(); // deep copying address return copy; } } // Copy constructor public class Person { String name; Address address; public Person(Person other) { this.name = other.name; this.address = new Address(other.address.city); } }
Key features:
clone() requires the implementation of the Cloneable interface and overriding the clone method.Question 1: If a class does not implement Cloneable, what happens when clone() is called?
clone() will throw CloneNotSupportedException if the object does not implement the Cloneable interface.
Question 2: Is cloning via clone() deep by default?
No, by default only shallow cloning is implemented; all references to objects are copied as they are.
Question 3: Is it possible to do without a copy constructor for deep cloning?
No, if you want to control the process, you need to explicitly define the copying of nested objects either through a constructor or manually in clone().
A developer used the standard clone() in the Order class with a List<Product> field. As a result, when the list of products was changed in the copy, the products in the original were also modified, leading to a bug.
Pros:
Cons:
The company implemented a copy constructor for all domain entities and additionally covered the cloning process for complex nested objects with tests.
Pros:
Cons: