Object Cloning in Java

In Java, object cloning refers to the process of creating a duplicate copy of an object. This allows you to create a new object with the same state (i.e., field values) as the original object. Cloning is particularly useful in scenarios where you want to create a copy of an object without modifying the original one, such as when you need to create backup copies or work with objects in a thread-safe manner.

What is Object Cloning?

Object cloning in Java can be achieved using the clone() method, which is provided by the Object class. By default, the clone() method performs a shallow copy of the object, meaning that it copies the reference values of the fields. If the fields themselves are references to other objects, those references are copied, not the actual objects.

To make use of cloning, the class must implement the Cloneable interface. This is a marker interface that indicates to the JVM that it is safe to clone instances of that class.

Shallow Copy vs. Deep Copy

  • Shallow Copy: The cloned object contains references to the same objects as the original object. Changes made to nested objects will affect both the original and cloned object.

  • Deep Copy: The cloned object contains copies of the objects referenced by the original object. Changes made to nested objects in the clone will not affect the original object.

Using the clone() Method

  1. Shallow Cloning: When you implement the clone() method in a class that implements Cloneable, Java creates a shallow copy of the object.

  2. Deep Cloning: You can override the clone() method to perform deep cloning, where you manually clone the objects referenced by the original object.

Example of Object Cloning (Shallow Copy)

class Person implements Cloneable {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // Performs shallow copy
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

public class TestCloning {
    public static void main(String[] args) {
        try {
            Person person1 = new Person("Alice", 25);
            Person person2 = (Person) person1.clone(); // Cloning person1

            System.out.println("Original: " + person1);
            System.out.println("Cloned: " + person2);

            // Modifying the cloned object
            person2.name = "Bob";
            person2.age = 30;

            System.out.println("\nAfter modifying cloned object:");
            System.out.println("Original: " + person1);
            System.out.println("Cloned: " + person2);

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

Output:

Original: Person [name=Alice, age=25]
Cloned: Person [name=Alice, age=25]

After modifying cloned object:
Original: Person [name=Alice, age=25]
Cloned: Person [name=Bob, age=30]

Explanation:

  • The Person class implements Cloneable and overrides the clone() method.

  • A shallow copy of person1 is created and stored in person2.

  • Modifying the name and age of the cloned object does not affect the original object, as it is a shallow copy of the object (the fields themselves are primitive data types, so they are copied directly).

Deep Cloning Example

To perform a deep copy, you need to override the clone() method to clone the fields that are references to other objects as well.

class Address implements Cloneable {
    String city;
    String state;

    public Address(String city, String state) {
        this.city = city;
        this.state = state;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class PersonWithAddress implements Cloneable {
    String name;
    Address address;

    public PersonWithAddress(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // Deep cloning the address field
        PersonWithAddress clonedPerson = (PersonWithAddress) super.clone();
        clonedPerson.address = (Address) address.clone(); // Cloning the address field
        return clonedPerson;
    }

    @Override
    public String toString() {
        return "PersonWithAddress [name=" + name + ", address=" + address.city + ", " + address.state + "]";
    }
}

public class TestDeepCloning {
    public static void main(String[] args) {
        try {
            Address address = new Address("New York", "NY");
            PersonWithAddress person1 = new PersonWithAddress("Alice", address);
            PersonWithAddress person2 = (PersonWithAddress) person1.clone(); // Deep cloning person1

            System.out.println("Original: " + person1);
            System.out.println("Cloned: " + person2);

            // Modifying the cloned object's address
            person2.address.city = "Los Angeles";
            person2.address.state = "CA";

            System.out.println("\nAfter modifying cloned object:");
            System.out.println("Original: " + person1);
            System.out.println("Cloned: " + person2);

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

Output:

Original: PersonWithAddress [name=Alice, address=New York, NY]
Cloned: PersonWithAddress [name=Alice, address=New York, NY]

After modifying cloned object:
Original: PersonWithAddress [name=Alice, address=New York, NY]
Cloned: PersonWithAddress [name=Alice, address=Los Angeles, CA]

Explanation:

  • In this example, the PersonWithAddress class performs deep cloning by cloning the address field.

  • After modifying the address of the cloned object, the original object's address remains unchanged, demonstrating that a deep copy was made.

Key Points About Cloning:

  • Shallow Copy: Only the top-level object is cloned, and references to other objects are copied (not the objects themselves).

  • Deep Copy: All objects referenced by the original object are also cloned, ensuring no shared references between the original and the cloned object.

  • Cloneable Interface: To allow cloning in Java, the class must implement the Cloneable interface. If the class does not implement this interface, calling clone() will throw a CloneNotSupportedException.

  • CloneNotSupportedException: This exception is thrown if the class does not implement Cloneable and you attempt to clone an object.

Conclusion

Object cloning is a powerful technique in Java for creating copies of objects, but it requires careful consideration of the type of copy you need: shallow or deep. By using the clone() method and understanding the concepts of shallow and deep cloning, you can manage object copies effectively in your programs.

0
Subscribe to my newsletter

Read articles from Mohammed Shakeel directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mohammed Shakeel
Mohammed Shakeel

I'm Mohammed Shakeel, an aspiring Android developer and software engineer with a keen interest in web development. I am passionate about creating innovative mobile applications and web solutions that are both functional and aesthetically pleasing.