0
Explore
0

Copy Constructor in Java

Updated on July 28, 2025

In Java, we create objects from classes using constructors. A constructor is a special method that helps us set up the object when it is created. Usually, we use a default constructor (which doesn’t take any input) or a parameterized constructor (which takes values to set the object’s data).

But there is another useful type of constructor called a copy constructor. A copy constructor is used to make a new object by copying the data from another object of the same class. This means instead of setting all the values again, we can just copy them from an existing object.

This is very helpful when we want to create a new object with the same values as another one, but we still want them to be separate objects in memory. So if we change one, the other won’t be affected.

In short, a copy constructor helps us duplicate objects easily and safely, which is useful when we work with real-world programs in Java.

What is a Copy Constructor?

A Copy Constructor is a special constructor in Java that takes an object of the same class as a parameter and copies its values into a new object.

Syntax of Copy Constructor

class Student {
    String name;
    int age;

    // Copy Constructor
    Student(Student s) {
        this.name = s.name;
        this.age = s.age;
    }
}

Why Use a Copy Constructor?

1. Cloning

Used to create an exact copy of an object with the same values.

2. Memory Management

Helps avoid sharing the same memory reference between multiple objects, preventing unwanted side effects.

3. Encapsulation

Keeps the data safe and protected by ensuring changes in one object don’t affect another.

4. Testing

Very useful for creating test objects with known, predefined data for unit testing or debugging.

How Copy Constructor Works Internally

  • It takes an object of the same class.
  • It copies each instance variable (primitive or reference).
  • For reference types, you can choose to copy the reference (shallow copy) or create a new object (deep copy).

Types of Copy Constructor (Conceptual Categories)

  1. Shallow Copy Constructor
  2. Deep Copy Constructor

Shallow Copy Constructor

A Shallow Copy Constructor creates a new object by:

  1. Copying the values of all primitive types directly (like int, float, boolean, etc.).
  2. Copying the references (memory addresses) of non-primitive types (like arrays, objects, or custom classes) — it does not create a new object for those references.

In simpler terms, both the original and copied object share the same sub-objects.

Use Case: When performance is more important than data safety, and the object doesn’t hold references to mutable objects.

What Happens Behind the Scenes?

Imagine a class like this:

class Address {
    String city;
    
    Address(String city) {
        this.city = city;
    }
}

class Person {
    String name;
    Address address;

    // Shallow Copy Constructor
    Person(Person p) {
        this.name = p.name;          // primitive (String is immutable)
        this.address = p.address;    // reference copy, not a new object
    }
}

In Memory (Heap & Stack):

  • When we create the original object Person p1, Java stores the Person object in heap memory.
  • The address field in p1 holds a reference (pointer) to another object (Address) in heap.
  • Now when we use the copy constructor to create Person p2, it copies:
    • The value of name (String is immutable, so it’s okay).
    • The reference to the same Address object, not a new one.

So, both p1.address and p2.address point to the same memory location.

Example with Output:

public class Test {
    public static void main(String[] args) {
        Address addr = new Address("Delhi");
        Person p1 = new Person("John", addr);
        Person p2 = new Person(p1);  // shallow copy

        p2.name = "Mike";
        p2.address.city = "Mumbai";  // shared object modified

        System.out.println(p1.name);           // John
        System.out.println(p1.address.city);   // Mumbai (Oops!)
    }
}

class Address {
    String city;

    Address(String city) {
        this.city = city;
    }
}

class Person {
    String name;
    Address address;

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

    // Shallow Copy Constructor
    Person(Person p) {
        this.name = p.name;
        this.address = p.address;  // shared reference
    }
}

Impact of Shallow Copy

  • Any change in the shared object (like address.city) will affect both original and copied objects.
  • This can lead to unexpected behavior if one object modifies a shared field.

When is Shallow Copy Okay?

  • When your class contains only primitive data types or immutable objects like String.
  • When you don’t plan to modify the sub-objects (like arrays, lists, custom objects).
  • When performance and memory usage is more important than data separation.

Advantages of Shallow Copy

  • Fast and easy to implement.
  • Uses less memory.
  • Good for read-only or immutable objects.

Disadvantages of Shallow Copy

  • Not safe if your class holds mutable objects (like arrays, ArrayList, or other custom objects).
  • Changes in one object may accidentally affect the other.

Shallow Copy Constructor – Summary

  • A Shallow Copy Constructor creates a new object but copies the reference of non-primitive (object) fields instead of duplicating them.
  • Primitive fields like int, boolean, etc. are copied directly.
  • Both the original and copied objects share the same internal object references.
  • If one object modifies a shared reference (like an array or object), the change will also reflect in the other.
  • It’s faster and uses less memory, but can lead to unexpected bugs when dealing with mutable or nested objects.
  • Best suited for simple objects or when performance is more important than data safety.

Deep Copy Constructor

A Deep Copy Constructor creates a completely independent copy of an object, including:

  • All primitive fields (like int, boolean, double) are copied directly.
  • All non-primitive fields (like arrays, lists, objects) are also copied, but as new objects, not just their memory references.

This means the original and the copy do not share any objects—they are 100% separate.

Use Case: When you want complete separation between the original and the copied object to avoid unwanted side effects.

Note: Java doesn’t automatically provide deep copy functionality—you have to write custom logic inside your copy constructor to handle it.

Behind the Scenes: What Happens in Memory?

Let’s say we have the following classes:

class Address {
    String city;

    Address(String city) {
        this.city = city;
    }
}

class Person {
    String name;
    Address address;

    // Deep Copy Constructor
    Person(Person p) {
        this.name = p.name;
        this.address = new Address(p.address.city);  // NEW Address object
    }
}

Step-by-Step:

  1. Person p1 is created with an Address object (say "Delhi").
  2. We then create Person p2 using the deep copy constructor.
  3. The constructor creates:
    • A new Person object (p2)
    • A new Address object with the same city value ("Delhi"), so it’s not shared with p1.

Result:
If we change p2.address.city, it won’t affect p1.address.city.

Example with Output

public class Test {
    public static void main(String[] args) {
        Address addr1 = new Address("Delhi");
        Person p1 = new Person("John", addr1);

        Person p2 = new Person(p1);  // Deep copy
        p2.name = "Mike";
        p2.address.city = "Mumbai";  // modifying copy's address

        System.out.println(p1.name);           // John
        System.out.println(p1.address.city);   // Delhi ✅ Unchanged
    }
}

class Address {
    String city;

    Address(String city) {
        this.city = city;
    }
}

class Person {
    String name;
    Address address;

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

    // Deep Copy Constructor
    Person(Person p) {
        this.name = p.name;
        this.address = new Address(p.address.city);  // Deep copy
    }
}

Why Use Deep Copy Constructor?

  • The original and the copied object are completely independent.
  • Any change in one object’s sub-objects (like address, list, etc.) does not affect the other.
  • Best suited when working with mutable or nested objects.

Advantages of Deep Copy

  • Ensures data integrity.
  • Prevents bugs due to shared references.
  • Ideal for complex objects with nested fields.

Disadvantages of Deep Copy

  • Slightly more complex to write (especially for deeply nested objects).
  • Consumes more memory and CPU time than shallow copy.
  • Needs to be updated manually if the class structure changes (e.g., new fields added).

Pro Tip:

If you have a lot of fields to deep copy (e.g., lists, maps, objects), consider using:

  • Serialization (convert object to byte stream and back)
  • Object Mapper (like Jackson or Gson)
  • Or implement your own deepClone() method.

Deep Copy Constructor – Summary

  • A Deep Copy Constructor creates a complete copy of an object, including all nested or internal objects.
  • It makes sure that the original and the copied object are fully separate in memory.
  • Any changes made to the copied object do not affect the original, and vice versa.
  • It is the safest method to duplicate objects when you’re dealing with mutable fields like arrays, lists, or custom class objects.
  • Deep copy helps avoid bugs caused by shared references, especially in large or complex applications.

Quick Tip

If your class has only primitive types or immutable fields, shallow copy is fine.
But if your class has custom objects, arrays, or collections, always prefer deep copy to avoid bugs.