Java programming, Copy Java object.

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.

Leave a Comment

Your email address will not be published. Required fields are marked *