Back to Object Cloning
Q2Question 2 of 2

How do you achieve deep cloning?

Key Concepts
  • Deep clone: create independent copies of all mutable reference fields, recursively
  • Method 1: manually clone each mutable field in the clone() method
  • Method 2: copy constructor that copies each field individually
  • Method 3: serialize to bytes and deserialize back (slow but automatic)
  • Immutable fields (String, BigDecimal, records) do not need deep copying

Definition

Deep cloning creates independent copies of all mutable reference fields so the original and clone share nothing. Use a copy constructor for the clearest approach.

Three approaches:

  • Manual deep clone: Override clone() and explicitly clone each mutable field: this.transactions = new ArrayList<>(original.transactions).
  • Copy constructor: new Account(otherAccount) copies each field. Clearest and most maintainable.
  • Serialization: Serialize the object to bytes, deserialize to a new instance. Automatic but slow.

Deep cloning a bank file: you photocopy every page including all attached sticky notes and receipts. Each copy is fully independent. Shallow cloning photocopies the cover page but leaves the sticky notes attached to the original.

Understanding the Concepts

Method 1: manual deep clone

JAVA
@Override
public Account clone() {
    try {
        Account copy = (Account) super.clone();  // shallow copy first
        copy.transactions = new ArrayList<>(this.transactions);  // deep copy the list
        // String and BigDecimal are immutable – no need to copy
        return copy;
    } catch (CloneNotSupportedException e) {
        throw new AssertionError(e);
    }
}

Method 2: copy constructor (preferred)

JAVA
public Account(Account other) {
    this.accountId = other.accountId;                   // immutable, share
    this.balance = other.balance;                       // immutable, share
    this.transactions = new ArrayList<>(other.transactions);  // deep copy
}

Method 3: serialization

JAVA
public static <T extends Serializable> T deepCopy(T obj) throws Exception {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    new ObjectOutputStream(bos).writeObject(obj);
    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    return (T) new ObjectInputStream(bis).readObject();
}

This copies everything automatically but is slow and requires all objects to be Serializable.

What should a senior engineer know?

  • Immutable objects never need deep copying. String, BigDecimal, LocalDate, records – safe to share.
  • The deeper the graph, the harder manual cloning gets. For complex object graphs, consider using a library like Apache Commons SerializationUtils.clone() or MapStruct for mapping.
  • The best solution is often immutability. If your objects are immutable, you never need to clone them.

What are common mistakes?

A common mistake is not understanding the concept fully before using it in production banking applications.

Wrong approach:

JAVA
package com.bank.service;

public class TransferService {
    public void transfer(String fromAccount, String toAccount, double amount) {
        // WRONG: using double for money
        double balance = getBalance(fromAccount);
        balance = balance - amount;
    }
    private double getBalance(String id) { return 10000.0; }
}

Correct approach:

JAVA
package com.bank.service;

import java.math.BigDecimal;

public class TransferService {
    public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
        // CORRECT: using BigDecimal for money
        BigDecimal balance = getBalance(fromAccount);
        balance = balance.subtract(amount);
    }
    private BigDecimal getBalance(String id) { return new BigDecimal("10000.00"); }
}

Always use BigDecimal for monetary calculations in banking systems to avoid floating-point precision errors.

What are the edge cases?

Edge cases can cause unexpected behavior in banking applications. Here are some to watch for.

JAVA
package com.bank.edge;

public class EdgeCaseDemo {
    public static void main(String[] args) {
        // Edge case 1: null handling
        String accountId = null;
        try {
            System.out.println(accountId.length());
        } catch (NullPointerException e) {
            System.out.println("Null account ID");
        }

        // Edge case 2: empty vs null
        String emptyId = "";
        System.out.println(emptyId.isEmpty());  // true
        System.out.println(emptyId == null);     // false

        // Edge case 3: boundary values
        java.math.BigDecimal zero = java.math.BigDecimal.ZERO;
        java.math.BigDecimal negativeBalance = new java.math.BigDecimal("-0.01");
        System.out.println(zero.compareTo(negativeBalance) > 0);  // true
    }
}
TEXT
Null account ID
true
false
true

Always test with null values, empty strings, boundary values, and negative amounts in banking applications.

What are common interview follow-up questions?

Interviewers often follow up with these related questions:

  • How does this concept apply to a high-throughput banking transaction system?
  • What are the performance implications in a production environment?
  • How would you debug issues related to this concept?
  • What tools would you use to monitor this in production?
  • How does this behave differently in Java 8 vs Java 17+?

When answering follow-up questions, always relate your answer back to practical experience. For example, at National Bank of India, Rajesh debugged a production issue related to this concept by using JFR recordings and heap dumps. Providing concrete examples from banking systems shows the interviewer that you understand both the theory and the practice.

Visual Overview

DIAGRAM
  Shallow clone: copy.transactions = original.transactions (shared!)
  Deep clone:    copy.transactions = new ArrayList<>(original.transactions)

  copy constructor > manual clone() > serialization
  Best: make objects immutable → no cloning needed

Key Takeaway

Deep cloning creates fully independent copies by copying every mutable reference field. The cleanest approach is a copy constructor that explicitly copies each field. Serialization automates the process but is slow. And the best solution is immutability: if your objects are immutable, you never need to clone them.

One sentence to remember: deep clone means copying all mutable fields independently – use a copy constructor, and prefer immutability to avoid cloning entirely.

---

Share
Use ← → arrow keys to navigate