How to copy objects in Java

Jinwon Park
3 min readNov 21, 2021

In Java, there is no simple operator to create a copy of an object. Here is an example of copying an object.

@Data
public class User {
String name;
int age;
Address address;
public User(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
}
@Data
public class Address {
String streetAddress;
String zipCode;
String countryCode;
public Address(String streetAddress, String zipCode, String countryCode) {
this.streetAddress = streetAddress;
this.zipCode = zipCode;
this.countryCode = countryCode;
}
}
@Test
public void main() {
User user1 = new User("Jinwon", 32, new Address("1st street", "123456", "KR"));
User user2 = new User(user1.getName(), user1.getAge(), user1.getAddress());
Assert.assertEquals(user1, user2); //checks if objects have same value
Assert.assertNotSame(user1, user2); //checks if object refer to same object
}

In the example above, I have created user1, and made a copy of user1 to user2. The main method runs without exception, meaning that user1 and user2 have same value, while not referring to same object.

But is it truly copied object?

Quick Answer: No.

I have made a shallow copy of the user1. Shallow copy is only copying the value of fields from one object to another.

Let’s say I change the streetAddress of user2 to “2nd street”.

user2.getAddress().setStreetAddress("2nd street");Assert.assertNotEquals(user1.getAddress().getStreetAddress(), user2.getAddress().getStreetAddress());

This will throw an exception, because both streetAddress for user1 and user2 have been changed to “2nd street”. More often than not, this is not the outcome we want when creating a copy of an object.

Deep copy solves the problem. Deep copy refers to copying each mutable object recursively. It makes a copy and returns a new object with no address being shared.

So, How do I implement deep copy?

Here are some typical solutions to make a deep copy.

  • Copy constructor
  • Cloneable interface
  • Cloner
  • Apache Commons Lang — SerializationUtils.clone()

Copy constructor

Simply add another constructor with parameter of the class itself.

public User(User copyReferenceUser) {
this(copyReferenceUser.getName(), copyReferenceUser.getAge(), new Address(copyReferenceUser.getAddress()));
}
public Address(Address copyReferenceAddress) {
this(copyReferenceAddress.getStreetAddress(), copyReferenceAddress.getZipCode(), copyReferenceAddress.getCountryCode());
}

Now, I can copy user2 from user1 using constructor.

@Test
public void main() {
...
User user2 = new User(user1);
Assert.assertNotSame(user1.getAddress(), user2.getAddress());
Assert.assertEquals(user1.getAddress(), user2.getAddress());
}

Now, even the Address field has same value, but not the same reference address.

This approach is simple and easy to implement. However, it will require separate constructor method for every mutable field of an object being copied. This can be very tedious for developers to implement.

Cloneable interface

Another way would be overriding the clone() method inherited from object class.

*Cloneable is marker interface, having no methods or constants. Marker interface provides run-time type information about objects to JVM and compiler.

@Data
public class Address implements Cloneable{
...
@Override
public Object clone() {
try {
return (Address) super.clone();
} catch(CloneNotSupportedException e) {
return new Address(this.getStreetAddress(), this.getZipcode(), this.getCountryCode());
}
}
}
@Data
public class User implements Cloneable{
...
@Override
public Object clone() {
User user;
try {
user = (User) super.clone();
} catch(CloneNotSupportedException e) {
user = new User(this.getName(), this.getAge(), this.getAddress());
}
user.setAddress((Address) this.getAddress().clone());
return user;
}

Now I can copy using clone() method.

@Test
public void main() {
...
User user2 = (User) user1.clone();
user2.getAddress().setStreetAddress("2nd street");
Assert.assertNotEquals(user1, user2);

This will not throw an exception, meaning the objects have been deep copied. While this is a good way to clone objects, it doesn’t change the fact that developers have to manually copy mutable variables. This can be run-time error prone in the more complex development environment.

Cloning Library

There are third party libraries that can help with these boiler plate codes. First is Cloning (https://github.com/kostaskougios/cloning) library, which provides deep clone method using java reflection.

...
Cloner cloner = new Cloner();
User user2 = cloner.deepClone(user1);

Our team has used this for a while, since Java 17, accessing and changing values in private fields by reflection is not allowed. In the long run, moving to libraries using serialization was the only option for now.

Apache Commons Lang — SerializationUtils.clone()

Apache Commons Lang provides deep clone method using serialization. All the classes being cloned needs to implement serializable.

...
User user2 = SerializationUtils.clone(user1);

Performance is relatively slower compared to cloning by reflection(Cloning library), but it is good enough, considering the alternative would be generating more and more boiler plate codes as the system complexity grows.

--

--