Cloning

Introduction

We know that objects as well as primitive data types can be passed to methods as a parameter. But there is a major difference between passing a primitive data type and object.

  1. When we pass a primitive, we just pass a copy of the primitive variable value.
  2. When we pass an object, we don’t pass a copy of that object, we just pass the reference of that object.

Let’s understand this with an example. Consider we want to add 10% interest to an amount.

public class JavaDemo {
    public static void main(String[] args) throws Exception {
        float amount = 100;
        applyInterest(amount);
        System.out.println(amount);    }
    
    public static void applyInterest(float amt) {
        amount = amount * 1.1f;
    }
}

Output: 100

From the output we can see, when we pass a primitive value to a method, a copy of that value is passed and the original value remains unchanged.

Now, pass an object for a similar operation.

public class Account {
    private float amount;

    public float getAmount() {
        return amount;
    }

    public void setAmount(float amount) {
        this.amount = amount;
    }
}
public class JavaDemo {
    public static void main(String[] args) throws Exception {
        Account account = new Account();
        account.setAmount(100);
        applyInterest(account);
        System.out.println(account.getAmount());
    }
    
    public static void applyInterest(Account account) {
        account.setAmount(account.getAmount() * 1.1f);
    }
}

Output: 110

From the output, we can see when we pass an object to a method, we do not pass a copy. Instead we pass the reference of that object and as a result the change inside method applyInterest() has altered the original account object.

So, you see, you are essentially passing the object around or in other words you are sharing the object. Most of the time this is actually fine and in fact this is what you want. But sometimes this may be dangerous.

Need for cloning

When the same object is shared among multiple parties, if someone alters the object value in an unexpected way, everyone will be affected. So, in some special scenario you may want to pass a copy of an object, not the original object to keep the original object unaffected. For example, let’s say you want to enable a sort of rollback feature. If an operation is successful, change the original object otherwise rollback the changes. To achieve that, you may write something like –

Account account = new Account();
account.setAmount(100);
Account copy = copyMethod(account); // A method to copy object
try {
    applyInterest(copy);
    account = copy;
} catch (Exception e) {
    // Do not assign copy to original object
}

Now the question is, how can you write the copyMethod(). Maybe we can do something like below –

public static Account copyMethod(Account account) {
    Account copy = new Account();
    copy.setAmount(account.getAmount());
    return copy;
}

This will work just fine. But imagine you have hundreds of fields in your class. Will you really be happy to call those many setters manually? Probably not. Rather, you will look for an alternate easy option. That option is the clone() method of Object class.

The clone() method

The clone() method creates and returns a copy of an original object. Let’s first look at the signature of this method –

public class Object {
    protected native Object clone() throws CloneNotSupportedException;
}

As we can see, the clone() method is declared in Object class as protected. So, to use it, we should override this method and probably change the access modifier to public.

public class Account {
    @Override
    public Object clone() throws CloneNotSupportedException {  
        return super.clone();  
    } 
}

Now, try to clone the account object.

Account account = new Account();
account.setAmount(100);
Account copy = (Account) account.clone();

Output:

Exception in thread "main" java.lang.CloneNotSupportedException: Account

The reason for this exception is, the clone() method in Object class checks if the class being cloned is cloneable. If not, it throws a CloneNotSupportedException.

To mark the Account class as cloneable, it must implement the java.lang.Cloneable interface. This interface is a marker interface meaning this interface doesn’t have any method to implement.

So, if you make the class Account as Cloneable and try to clone as earlier, it will work just fine.

public class Account implements Cloneable {
    @Override
    public Object clone() throws CloneNotSupportedException {  
        return super.clone();  
    } 
}
Shallow Copy

Let’s assume the Account class contains both primitive type and reference type.

public class Account implements Cloneable {
    private float amount;
    private CheckBook checkBook;
}
public class CheckBook {
    private int noOfPages;
}

When you create an instance of Account class, two objects will be created in Heap memory one for Account and another for CheckBook and the checkBook reference variable will refer to the CheckBook object in heap.

If you clone this account object, the clone() method of the object class will copy the primitive values to the cloned object, but rather than creating a new CheckBook object, the reference variable will be pointing to the same checkBook object in the heap.

So, the cloned object is called the shallow copy of the original object as they both share the same reference object in Heap.

Deep copy

In some situations, along with the object, you may want to create a separate copy of all the objects referred by the reference variables. For that you need a deep copy. A deep copy can be created using various approaches, like –

  1. Copy constructor
  2. Cloneable Interface
  3. Serialization

1. Copy constructor

A copy constructor in a Java class is a constructor that creates a new object using another object of the same class. Let’s see an example of how a copy constructor can help us to create a deep copy.

public class CheckBook {
    private int noOfPages;
    
    public CheckBook(CheckBook checkBook) { // copy constructor
        this(checkBook.noOfPages);
    }
    
    public CheckBook(int noOfPages) {
        this.noOfPages = noOfPages;
    }
}
public class Account {
    private float amount;
    private CheckBook checkBook;
    
    public Account(Account account) { // copy constructor
        this(account.amount, new CheckBook(account.checkBook));
    }
    
    public Account(float amount, CheckBook checkBook) {
        this.amount = amount;
        this.checkBook = checkBook;
    }
}

As you can see, using copy constructor, along with Account, we are also creating a new instance of CheckBook and assigning that to the reference variable. So, creating deep copy is as simple as –

CheckBook checkBook = new CheckBook(30);
Account account = new Account(100, checkBook);
Account deepCopy = new Account(account);

2. Cloneable interface

We can make both CheckBook and Account class Cloneable and override the clone() method to create a deep copy.

public class CheckBook implements Cloneable {
    private int noOfPages;
    
    public CheckBook(int noOfPages) {
        this.noOfPages = noOfPages;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Account implements Cloneable {
    private float amount;
    private CheckBook checkBook;
    
    public Account(float amount, CheckBook checkBook) {
        this.amount = amount;
        this.checkBook = checkBook;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Account copy = (Account) super.clone();
        copy.checkBook = (CheckBook) checkBook.clone();
        return copy;
    }
}

To make a deep copy we just have to clone an Account object. Internally it will clone the CheckBook object and assign that cloned object to the reference variable.

CheckBook checkBook = new CheckBook(30);
Account account = new Account(100, checkBook);
Account deepCopy = (Account) account.clone();

3. Serialization

Above solutions may look perfect, but writing additional constructors or implementing the clone method may be tedious if a class contains a lot of reference variables. So, a reusable solution would be to serialize an object and then deserialize it to get a deep copy.

public class CheckBook implements Serializable {
    private int noOfPages;
    
    public CheckBook(int noOfPages) {
        this.noOfPages = noOfPages;
    }
}
public class Account implements Serializable {
    private float amount;
    private CheckBook checkBook;
    
    public Account(float amount, CheckBook checkBook) {
        this.amount = amount;
        this.checkBook = checkBook;
    }
}

Now create a reusable method to create a deep copy of any Serializable object and call that method.

public class JavaDemo {
    public static void main(String[] args) throws Exception {
        CheckBook checkBook = new CheckBook(30);
        Account account = new Account(100, checkBook);
        Account deepCopy = deepCopy(account);
    }
    
    public static <T> T deepCopy(Serializable obj) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(baos);
        out.writeObject(obj);
        out.close();
        
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream in = new ObjectInputStream(bais);
        T deepCopy = (T) in.readObject();
        in.close();
        return deepCopy;
    }
}

That’s it for now. Hope you have enjoyed this tutorial. If you have any doubt, please ask in the comment section. I will try to answer that as soon as possible. Till then, bye bye.