Copy objects in Java
This post will discuss various methods to copy objects in Java. Copying an object is creating a copy of an existing object in order to modify or move the copied object without impacting the original object.
Unlike C++, Java objects are not passed by value, but their references are passed by value instead. Consider the following code snippets,
Object ob1 = new Object();
Object ob2 = ob1;
Both ob1 and ob2 points to the same object now as this is just a reference to the original data; no copying occurs here.
Object ob = new Object();
fun(ob);
This is also just a reference to the original data as methods in Java are always pass by value. However, it is the value of the reference variable that is being passed.
int a = 5;
int b = a;
Since the data is primitive here, it simply copies the value of the primitive type.
Sometimes it is essential to create a copy of an object to use in the application. This post will discuss several ways to achieve that. Before we begin, let’s talk about shallow copy and deep copy of objects.
1.Shallow copy (field-by-field copy)
One method of copying an object is the shallow copy in which we allocate a new, uninitialized object and copy all fields (attributes) from the original object in it. This does not always result in desired behavior when the field value is a reference to an object, as it copies the reference, hence referring to the same object as an original object does. The referenced objects are thus shared, so if one object is modified, the change is visible in the other.

2. Deep copy
An alternative to shallow copy is a deep copy, where new objects are created for any referenced objects rather than references to objects being copied. A deep copy is a preferable approach over a shallow copy.

There are several approaches to copy an object, as discussed below:
1. Using Copy Constructor or Factory
With the help of Copy Constructor, we can define the actions performed by the compiler when copying a class object. Any copy constructor implementation should perform deep copy for any referenced objects in the class by creating new objects and copy the values for the primitive and immutable types. It usually accepts only one parameter that is just another instance of the same class.
We can also use the static copy factory method to essentially do the same thing as the copy constructor method. Both approaches are shown below:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
import java.util.Arrays; import java.util.HashSet; import java.util.Set; class Student { private String name; private int age; private Set<String> subjects; public Student(String name, int age, Set<String> subjects) { this.name = name; this.age = age; this.subjects = subjects; } // Copy constructor public Student(Student student) { this.name = student.name; this.age = student.age; // shallow copy // this.subjects = student.subjects; // deep copy – create a new instance of `HashSet` this.subjects = new HashSet<>(student.subjects); } // Copy factory public static Student newInstance(Student student) { return new Student(student); } @Override public String toString() { return Arrays.asList(name, String.valueOf(age), subjects.toString()).toString(); } public Set<String> getSubjects() { return subjects; } } // Copy objects in Java – Copy constructor and factory example class Main { public static void compare(Object ob1, Object ob2) { if (ob1 == ob2) { System.out.println("Shallow Copy"); } else { System.out.println("Deep Copy"); } }; public static void main(String[] args) { Student student = new Student("Jon Snow", 22, new HashSet<String>( Arrays.asList("Maths", "Science", "English")) ); Student clone = new Student(student); System.out.println("Calling Copy Constructor: Clone is " + clone); compare(student.getSubjects(), clone.getSubjects()); clone = Student.newInstance(student); System.out.println("\nCalling Copy Factory: Clone is " + clone); compare(student.getSubjects(), clone.getSubjects()); } } |
Output:
Calling Copy Constructor: Clone is [Jon Snow, 22, [Maths, English, Science]]
Deep Copy
Calling Copy Factory: Clone is [Jon Snow, 22, [Maths, English, Science]]
Deep Copy
2. Using clone() method
⮚ Using Object.clone() method
If the concrete type of the object to be cloned is known in advance, we can use the Object.clone() method, which creates and returns a copy of the object. The prototype of the Object.clone() is
protected Object clone() throws CloneNotSupportedException
All classes involved must implement the Cloneable interface, otherwise CloneNotSupportedException will be thrown. The default implementation Object.clone() is shown below:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class Student implements Cloneable { private String name; // immutable field private int age; // primitive field private Map<String, Integer> map; // mutable field public Student(String name, int age) { this.name = name; this.age = age; this.map = new HashMap<String, Integer>() {{ put(name, age); }}; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); // return shallow copy } } |
This method returns a shallow copy of the Student object. In shallow copy, if the field is a primitive type, it copies its value; otherwise, if the field value is a reference to an object, it copies the reference, hence referring to the same object. Now, if one of these objects is modified, the change is visible in the other.
In deep copy, new objects are created for any referenced objects, unlike shallow copy where referenced objects are shared. For deep cloning, the clone() method must modify mutable non-final fields of the object returned by super.clone() before returning to the caller, as shown below:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
class Student implements Cloneable { private String name; // immutable field private int age; // primitive field private Map<String, Integer> map; // mutable field public Student(String name, int age) { this.name = name; this.age = age; this.map = new HashMap<String, Integer>() {{ put(name, age); }}; } @Override public Student `clone()` throws CloneNotSupportedException { Student student = (Student) super.clone(); // primitive fields are ignored, as their content is already copied // immutable fields like string are ignored // create new objects for any non-primitive, mutable fields student.map = new HashMap<>(map); return student; // return deep copy } } // in the main method Student student = new Student("Jon Snow", 22); try { Student clone = student.clone(); } catch (CloneNotSupportedException ex) { ex.printStackTrace(); } |
This approach is discussed here in detail.
⮚ Using Apache SerializationUtils’s clone() method
Apache Commons Lang SerializationUtils also provides an implementation of the clone() method that performs deep cloning using serialization. It is very helpful as deep cloning using Object‘s clone method is time-consuming and error-prone for complex objects. It can be called as:
Student clone = (Student) SerializationUtils.clone(student);
3. Using Serialization
Serialization is the process of converting an object into a sequence of bytes and rebuilding those bytes later into a new object.
As seen before, deep copy using Object.clone() is very tedious to implement, error-prone, and difficult to maintain. Also, the Object.clone() method will not work if we assign a value to a final field. We can use Serialization to address these issues.
⮚ Using Serializable Interface
Java provides automatic serialization, which requires that the object be marked by implementing the java.io.Serializable interface. Implementing the interface marks the class as “okay to serialize”, and Java then handles serialization internally. Serialization won’t work on transient fields.
There are no serialization methods defined on the Serializable interface, but we can use ObjectOutputStream.writeObject() method to convert the object into a serialized form, and a corresponding ObjectInputStream.readObject() method to recreate an object from that representation. The result will be a completely distinct object, with completely distinct referenced objects.
Java Code for Serialization:
|
1 2 3 4 5 6 |
// Create a byte array output stream and use it to create an object output stream ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(student); // serialize oos.flush(); |
Java Code for Deserialization:
|
1 2 3 4 5 6 7 8 |
// `toByteArray()` creates and returns a copy of the stream's byte array byte[] bytes = bos.toByteArray(); // Create a byte array input stream and use it to create an object input stream ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bis); clone = (Student) ois.readObject(); // deserialize & typecast |
⮚ Using Apache commons SerializationUtils
We can also use the SerializationUtils class provided by Apache Commons Lang to assist with the serialization process. It provides serialize() and deserialize() methods to serialize and deserialize an object, respectively. Code is shown below:
|
1 2 3 4 5 |
// Serializes `Student` object to a `byte[]` array byte[] bytes = SerializationUtils.serialize(student); // Deserializes `Student` object from an array of bytes Student clone = (Student) SerializationUtils.deserialize(bytes); |
4. Converting to JSON
It is widespread to receive JSON response from a Java web service instead of XML. Java doesn’t have any built-in support to create and process JSON data, but several good open-source libraries such as Google’s Gson, Jackson, and JSON-simple.
⮚ Using Google’s GSON Library
We can use Google’s GSON library (which uses reflection) to serialize a Java object to JSON string and then deserialize it to get a new object. This will perform a deep copy with a few restrictions. We know that a transient keyword indicates that a field should not be serialized. So this method will not copy transient fields. Also, this will not work on objects with recursive references.
Gson does not require additional modifications to classes of serialized/deserialized objects. Gson documentation states that the class to have defined default no-args constructor or have registered InstanceCreator.
The following code will produce a deep copy of the Student object by:
- Get an instance of Gson Object.
- Use
gson.toJson()to serialize Object (convert Student Object to JSON string). - Then use
gson.fromJson()to unserialize Object (convert JSON string back to Student Object).
|
1 2 3 |
Gson gson = new Gson(); String jsonString = gson.toJson(student); Student clone = gson.fromJson(jsonString, Student.class); |
⮚ Using Jackson library
Another simple approach is to use the Jackson library to serialize complex Java Object to JSON and deserialize it back. It requires the class to have a default no-args constructor to instantiate Java Object from JSON string and only works on Java SE 1.5 or more. Unlike Gson, Jackson will require getters for all private fields, otherwise serialization and deserialization won’t work.
The following steps will produce a deep copy of the Student object by:
- Get an instance of
ObjectMapperObject. - Use
mapper.writeValueAsString()to serialize Object (convert Student Object to JSON string). - Then use
mapper.readValue()to unserialize Object (convert JSON string back to Student Object).
|
1 2 3 |
ObjectMapper mapper = new ObjectMapper(); String jsonString = mapper.writeValueAsString(person); Student clone = mapper.readValue(jsonString, Student.class); |
Please note that both these methods have relatively poor performance than the copy constructor or Object.clone() method discussed above.
That’s all about copying objects in Java.
References:
https://en.wikipedia.org/wiki/Object_copying#Copying_in_Java
https://en.wikipedia.org/wiki/Clone_(Java_method)
Thanks for reading.
To share your code in the comments, please use our online compiler that supports C, C++, Java, Python, JavaScript, C#, PHP, and many more popular programming languages.
Like us? Refer us to your friends and support our growth. Happy coding :)