Object Cloning is the process of creating an exact copy of an object in a separate memory location. Understanding the difference between Shallow Copying (sharing nested references) and Deep Copying (recursively duplicating object graphs) is vital for state preservation, preventing side-effects, and memory safety.
Shallow copying creates a new top-level object and copies all primitive fields. However, for reference fields (pointers or object handles), it copies only the memory address — meaning both original and cloned objects share references to the exact same nested objects.
class Address {
String city;
Address(String city) { this.city = city; }
}
class Person implements Cloneable {
String name;
Address address; // Reference field
Person(String name, Address address) {
this.name = name;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // Shallow copy: address reference is shared
}
}
public class Main {
public static void main(String[] args) throws Exception {
Address addr = new Address("Mumbai");
Person p1 = new Person("Rahul", addr);
Person p2 = (Person) p1.clone(); // Shallow copy
p2.address.city = "New Delhi"; // Modifies shared Address
System.out.println(p1.name + " lives in " + p1.address.city); // New Delhi
System.out.println(p2.name + " lives in " + p2.address.city); // New Delhi
}
}
#include <iostream>
#include <string>
using namespace std;
class Address {
public:
string city;
Address(string c) : city(c) {}
};
class Person {
public:
string name;
Address* address; // Pointer field
Person(string n, Address* a) : name(n), address(a) {}
// Copy Constructor (Shallow Copy: copies the pointer address)
Person(const Person& other) {
this->name = other.name;
this->address = other.address; // Shared pointer reference
}
};
int main() {
Address* addr = new Address("Mumbai");
Person* p1 = new Person("Rahul", addr);
Person* p2 = new Person(*p1); // Shallow clone
p2->address->city = "New Delhi"; // Modifies shared Address
cout << p1->name << " lives in " << p1->address->city << "\n"; // New Delhi
cout << p2->name << " lives in " << p2->address->city << "\n"; // New Delhi
delete p1;
delete p2;
delete addr;
return 0;
}
import copy
class Address:
def __init__(self, city):
self.city = city
class Person:
def __init__(self, name, address):
self.name = name
self.address = address
def main():
addr = Address("Mumbai")
p1 = Person("Rahul", addr)
p2 = copy.copy(p1) # Shallow copy via copy module
p2.address.city = "New Delhi" # Modifies shared Address
print(p1.name + " lives in " + p1.address.city) # New Delhi
print(p2.name + " lives in " + p2.address.city) # New Delhi
if __name__ == "__main__":
main()
Deep copying creates a completely independent clone. It copies the top-level object and recursively duplicates all nested objects into fresh memory locations. Changes to the clone's nested fields have zero impact on the original object.
class Address implements Cloneable {
String city;
Address(String city) { this.city = city; }
@Override
protected Object clone() throws CloneNotSupportedException {
return new Address(this.city);
}
}
class Person implements Cloneable {
String name;
Address address;
Person(String name, Address address) {
this.name = name;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone(); // Shallow copy outer
cloned.address = (Address) this.address.clone(); // Deep copy nested
return cloned;
}
}
public class Main {
public static void main(String[] args) throws Exception {
Address addr = new Address("Mumbai");
Person p1 = new Person("Rahul", addr);
Person p2 = (Person) p1.clone(); // Deep copy
p2.address.city = "New Delhi";
System.out.println(p1.name + " lives in " + p1.address.city); // Mumbai
System.out.println(p2.name + " lives in " + p2.address.city); // New Delhi
}
}
#include <iostream>
#include <string>
using namespace std;
class Address {
public:
string city;
Address(string c) : city(c) {}
Address(const Address& other) : city(other.city) {}
};
class Person {
public:
string name;
Address* address;
Person(string n, Address* a) : name(n), address(a) {}
// Copy Constructor (Deep Copy: allocates new Address on heap)
Person(const Person& other) {
this->name = other.name;
this->address = new Address(*other.address); // Deep copy
}
~Person() {
delete address; // Safe memory cleanup for owned object
}
};
int main() {
Address* addr = new Address("Mumbai");
Person* p1 = new Person("Rahul", addr);
Person* p2 = new Person(*p1); // Deep copy
p2->address->city = "New Delhi";
cout << p1->name << " lives in " << p1->address->city << "\n"; // Mumbai
cout << p2->name << " lives in " << p2->address->city << "\n"; // New Delhi
delete p1;
delete p2;
delete addr;
return 0;
}
import copy
class Address:
def __init__(self, city):
self.city = city
class Person:
def __init__(self, name, address):
self.name = name
self.address = address
def main():
addr = Address("Mumbai")
p1 = Person("Rahul", addr)
p2 = copy.deepcopy(p1) # Deep copy via copy module
p2.address.city = "New Delhi"
print(p1.name + " lives in " + p1.address.city) # Mumbai
print(p2.name + " lives in " + p2.address.city) # New Delhi
if __name__ == "__main__":
main()
Languages provide specific hooks for fine-tuning cloning logic:
Object.clone() and implement
Cloneable marker interface. Throws CloneNotSupportedException if
marker is missing.
Person(const Person&)), Copy Assignment Operator (operator=), or polymorphic
virtual Person* clone() const.
__copy__(self) for custom shallow cloning
and __deepcopy__(self, memo) for deep cloning. The memo dictionary
prevents infinite recursion on cyclic graphs.
| Aspect | Shallow Copying | Deep Copying |
|---|---|---|
| Outer Object | New outer object created | New outer object created |
| Nested Objects | Shared by reference (same memory address) | Recursively cloned into new memory locations |
| Independence | Mutating nested data affects original object | Completely independent memory graph |
| Java Mechanism | Default super.clone() |
Manual clone() on reference fields |
| C++ Mechanism | Default compiler copy constructor (pointer copy) | Custom copy constructor with new allocation |
| Python Mechanism | copy.copy(obj) / __copy__ |
copy.deepcopy(obj) / __deepcopy__ |
What happens in Java if a class calls super.clone() without implementing
Cloneable?
Why does Python's __deepcopy__(self, memo) method accept a
memo parameter?
Cloning ⇒ Creating duplicate object in separate memory location
Use Cases ⇒ State snapshots ; undo/redo ; prototype pattern ; avoiding unwanted reference sharing
Behavior ⇒ New outer object ; reference/pointer fields shared with original
Side-effect ⇒ Modifying nested mutable object via clone mutates original as well
Tools ⇒ Java super.clone() ; C++ default copy ctor ; Python
copy.copy()
Behavior ⇒ New outer object + recursively cloned nested objects
Independence ⇒ Completely isolated memory graph ; no shared reference side-effects
Tools ⇒ Java manual nested.clone() ; C++
new Address(*other.addr) ; Python copy.deepcopy()
Primary source: Oracle Java Tutorials — Cloneable & Object.clone(), cppreference — Copy Constructors & Python Docs — copy module. Ask me anything that's unclear.
Questions? Ask your agent — you can follow up on any concept, quiz answer, or how this applies to a specific coding problem. Review the Java glossary or syntax quick reference for a compressed overview.