A developer who wants to copy an object usually faces problems when changing the original object’s value. Then, the clone object’s value has been changing, too, because, in Java programming, the objects reference each other. To prevent this problem, the developer should copy objects using several methods, such as Serialization.
For example, copy an object.
1. The developer copies objects using Cloneable.
Create a Java bean that implements Cloneable.
import lombok.Getter; import lombok.Setter; @Getter @Setter public class PersonClone implements Cloneable { private String name; private int age; public PersonClone(String name, int age) { this.name = name; this.age = age; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } }
Create a Java class to copy the Java object bean using the clone method.
import com.example.beans.PersonClone; public class CopyObjectClone { public static void main(String[] args) { try { PersonClone original = new PersonClone("John", 30); PersonClone clone = (PersonClone) original.clone(); System.out.println("original = "+original); System.out.println("clone = "+clone); System.out.println("Change age in original object to 70 and name to Jane"); original.setName("Jane"); original.setAge(70); System.out.println("original = "+original); System.out.println("clone = "+clone); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
original = Person{name='John', age=30} clone = Person{name='John', age=30} Change age in original object to 70 and name to Jane original = Person{name='Jane', age=70} clone = Person{name='John', age=30}
In conclusion, the clone object does not reference the original object.
It requires careful handling, especially when dealing with deep cloning.
2. The developer copies objects using the Constructor.
Create a Java bean.
@Getter @Setter public class PersonConstructor { private String name; private int age; public PersonConstructor(String name, int age) { this.name = name; this.age = age; } public PersonConstructor(PersonConstructor other) { this.name = other.name; this.age = other.age; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } }
Create a Java class to copy the Java object bean using a Constructor.
import com.example.beans.PersonConstructor; public class CopyUsingConstructor { public static void main(String[] args) { PersonConstructor original = new PersonConstructor("John", 30); PersonConstructor clone = new PersonConstructor(original); System.out.println(original); System.out.println(clone); } }
original = Person{name='John', age=30} clone = Person{name='John', age=30} Change age in original object to 70 and name to Jane original = Person{name='Jane', age=70} clone = Person{name='John', age=30}
In conclusion, the clone object does not reference the original object.
This is straightforward and provides more control over the cloning process.
3. The developer copies objects using Serialization.
Create a Java bean that implements Serializable.
import lombok.Getter; import lombok.Setter; import java.io.*; @Getter @Setter public class PersonSerialization implements Serializable { private String name; private int age; public PersonSerialization(String name, int age) { this.name = name; this.age = age; } public PersonSerialization deepClone() { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeObject(this); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream in = new ObjectInputStream(bis); return (PersonSerialization) in.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); return null; } } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } }
Create a Java class to copy the Java object bean using the “deepClone” method.
import com.example.beans.PersonSerialization; public class CopyUsingSerialization { public static void main(String[] args) { PersonSerialization original = new PersonSerialization("John", 30); PersonSerialization clone = original.deepClone(); System.out.println("original = "+original); System.out.println("clone = "+clone); System.out.println("Change age in original object to 70 and name to Jane"); original.setName("Jane"); original.setAge(70); System.out.println("original = "+original); System.out.println("clone = "+clone); } }
original = Person{name='John', age=30} clone = Person{name='John', age=30} Change age in original object to 70 and name to Jane original = Person{name='Jane', age=70} clone = Person{name='John', age=30}
In conclusion, the clone object does not reference the original object.
This is a deep cloning technique, though it can be slower and more resource-intensive.
4. The developer copies objects using Apache Commons Lang.
Create a Java bean that implements Serializable.
import java.io.Serializable; @Getter @Setter public class PersonApacheCommonsLang implements Serializable { private String name; private int age; public PersonApacheCommonsLang(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } }
Create a Java class to copy the Java object bean using SerializationUtils.
public class CopyUsingApacheCommonsLang { public static void main(String[] args) { PersonApacheCommonsLang original = new PersonApacheCommonsLang("John", 30); PersonApacheCommonsLang clone = SerializationUtils.clone(original); System.out.println("original = "+original); System.out.println("clone = "+clone); System.out.println("Change age in original object to 70 and name to Jane"); original.setName("Jane"); original.setAge(70); System.out.println("original = "+original); System.out.println("clone = "+clone); } }
original = Person{name='John', age=30} clone = Person{name='John', age=30} Change age in original object to 70 and name to Jane original = Person{name='Jane', age=70} clone = Person{name='John', age=30}
In conclusion, the clone object does not reference the original object.
5. The developer copies objects using GSON.
Create a Java bean.
import lombok.Getter; import lombok.Setter; @Getter @Setter public class PersonGson { private String name; private int age; public PersonGson(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } }
Create a Java class to copy the Java object bean using GSON.
import com.example.beans.PersonGson; import com.google.gson.Gson; public class CopyUsingGson { public static void main(String[] args){ PersonGson original = new PersonGson("John", 30); Gson gson = new Gson(); // Serialize the object to JSON String originalJson = gson.toJson(original); // Deserialize the JSON back to an object PersonGson clone = gson.fromJson(originalJson, PersonGson.class); System.out.println("original = "+original); System.out.println("clone = "+clone); System.out.println("Change age in original object to 70 and name to Jane"); original.setName("Jane"); original.setAge(70); System.out.println("original = "+original); System.out.println("clone = "+clone); System.out.println("Change age in clone object to 35 and name to Jack"); clone.setName("Jack"); clone.setAge(35); System.out.println("original = "+original); System.out.println("clone = "+clone); } }
original = Person{name='John', age=30} clone = Person{name='John', age=30} Change age in original object to 70 and name to Jane original = Person{name='Jane', age=70} clone = Person{name='John', age=30} Change age in clone object to 35 and name to Jack original = Person{name='Jane', age=70} clone = Person{name='Jack', age=35}
In conclusion, the clone object does not reference the original object.
Gson handles the serialization and deserialization of nested objects automatically.
Limitations of copying using GSON.
The limit of GSON is the same object inside the object as a variable array list.
Create a Java bean.
@Getter @Setter public class PersonGsonStack { private String name; private int age; private List<PersonGsonStack> details; public PersonGsonStack(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } }
Create a Java class to copy the Java object bean using GSON.
import com.example.beans.PersonGson; import com.example.beans.PersonGsonStack; import com.google.gson.Gson; import java.util.ArrayList; public class CopyUsingGsonStack { public static void main(String[] args){ PersonGsonStack original = new PersonGsonStack("John", 30); original.setDetails(new ArrayList<>()); original.getDetails().add(original); Gson gson = new Gson(); // Serialize the object to JSON String originalJson = gson.toJson(original); // Deserialize the JSON back to an object PersonGsonStack clone = gson.fromJson(originalJson, PersonGsonStack.class); System.out.println("original = "+original); System.out.println("clone = "+clone); } }
Exception in thread "main" java.lang.StackOverflowError at java.base/java.lang.InternalError.<init>(InternalError.java:86) at java.base/jdk.internal.reflect.MethodHandleObjectFieldAccessorImpl.get(MethodHandleObjectFieldAccessorImpl.java:63) at java.base/java.lang.reflect.Field.get(Field.java:444) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$2.write(ReflectiveTypeAdapterFactory.java:240) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:490) at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:1521) ...
In conclusion, the developer gets an error when executing the method because of a stack overflow that Gson goes into an infinite loop in the Java object.
The developer can customize the method to limit how deep Gson goes into Java Bean using GsonBuilder or skip the variable using ExclusionStrategy.
Copy a Java bean from a different Java bean.
The developer can copy beans from the other beans that share the same variable names using reflection or third-party libraries.
Using Reflection.
Manually iterates through fields in the source object and copies them to the destination object if they have the same name.
@Getter @Setter public class SourcePerson { private String name; private int age; public SourcePerson(String name, int age) { this.name = name; this.age = age; } }
@Getter @Setter public class DestinationPerson { private String name; private int age; @Override public String toString() { return "DestinationPerson{name='" + name + "', age=" + age + "}"; } }
import java.lang.reflect.Field; public class ReflectionCopy { public static void main(String[] args) throws IllegalAccessException { SourcePerson source = new SourcePerson("John", 30); DestinationPerson destination = new DestinationPerson(); for (Field field : source.getClass().getDeclaredFields()) { field.setAccessible(true); Field destField; try { destField = destination.getClass().getDeclaredField(field.getName()); destField.setAccessible(true); destField.set(destination, field.get(source)); } catch (NoSuchFieldException e) { // Field does not exist in destination, skip it } } source.setName("Jane"); source.setAge(40); System.out.println(destination); } }
Run a Java class using reflection.
DestinationPerson{name='John', age=30}
Using Apache Commons BeanUtils.
It provides a simple way to copy properties between two beans with the same property names.
Add Maven dependency.
<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils --> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.4</version> </dependency>
import org.apache.commons.beanutils.BeanUtils; public class BeanUtilsCopy { public static void main(String[] args) { SourcePerson source = new SourcePerson("John", 30); DestinationPerson destination = new DestinationPerson(); try { BeanUtils.copyProperties(destination, source); } catch (Exception e) { e.printStackTrace(); } source.setName("Jane"); source.setAge(40); System.out.println(destination); } }
Run a Java class using BeanUtils.
DestinationPerson{name='John', age=30}
Using ModelMapper.
ModelMapper is particularly useful for more complex scenarios, such as deep copying and object transformation.
Add Maven dependency.
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper --> <dependency> <groupId>org.modelmapper</groupId> <artifactId>modelmapper</artifactId> <version>3.2.1</version> </dependency>
import org.modelmapper.ModelMapper; public class ModelMapperCopy { public static void main(String[] args) { SourcePerson source = new SourcePerson("John", 30); DestinationPerson destination = new DestinationPerson(); ModelMapper modelMapper = new ModelMapper(); modelMapper.map(source, destination); source.setName("Jane"); source.setAge(40); System.out.println(destination); } }
Run a Java class using ModelMapper.
DestinationPerson{name='John', age=30}
In conclusion, these three approaches have the same result for copying objects. The clone object does not reference the source object.
Finally
The developer should choose a copy object method suitable for the project. Each copy method has pros and cons.