A shallow copy makes a new object but reuses the same nested objects, so the copy and the original still share their inner data. A deep copy duplicates everything, including every nested object, so the two are fully independent. The difference only matters when an object holds other mutable objects inside it. In Python, copy.copy() is shallow and copy.deepcopy() is deep.
Shallow copy vs deep copy is a favourite interview question, and a common source of bugs. Both create a new object, so they look the same until you change a nested value. Then a shallow copy surprises you: the change leaks into the original.
The reason is how each one treats the objects inside the object. A shallow copy duplicates only the top level and shares what is nested. A deep copy walks the whole structure and duplicates every level. That single difference decides whether the copy is truly independent.
This guide defines each kind of copy on its own, shows the difference in memory, and gives working code in Python and Java. It also covers when the distinction matters and the interview questions that test it.

What is a Shallow Copy?
A shallow copy creates a new object and copies the top-level values into it. For any nested object, it copies only the reference, not the nested object itself. So the new object and the original point to the same inner objects.
This means the outer object is independent, but the inner objects are shared. If you change a nested mutable value through one copy, the change shows up in the other. For a flat structure of simple values, that never bites you. For a structure with lists, dictionaries, or other objects inside, it can.
In Python, copy.copy() makes a shallow copy. A list slice a[:], list(a), and a.copy() are shallow too. In Java, the default Object.clone() is a shallow copy.
What is a Deep Copy?
A deep copy creates a new object and then recursively copies every nested object as well. Nothing is shared. The copy is a complete, independent duplicate, all the way down.
Because it rebuilds the whole structure, a deep copy is safe to change without touching the original. The trade-off is cost: it does more work and uses more memory, and it must handle nested references carefully, including cycles where an object refers back to itself.
In Python, copy.deepcopy() makes a deep copy and handles cycles for you. In Java, there is no built-in deep copy; you write it yourself by cloning each nested object, using a copy constructor, or serializing and deserializing the object.
Assignment vs Copy: How It Works

It helps to separate three operations that look similar but differ in memory.
- Assignment (
b = a) copies nothing. Both names refer to the one object, so a change through either is visible through both. - Shallow copy makes a new outer object, but its nested fields still point to the original nested objects.
- Deep copy makes a new outer object and new nested objects, so nothing is shared.
So assignment shares the whole object, a shallow copy shares only the inner objects, and a deep copy shares nothing. Understanding this memory model is closely tied to how a program uses the stack and the heap.
Shallow Copy vs Deep Copy: Key Differences

| Aspect | Shallow Copy | Deep Copy |
|---|---|---|
| What is copied | The top-level object only | The object and every nested object |
| Nested objects | Shared (same references) | Duplicated (independent) |
| Independence | Partial; inner data is shared | Full; nothing is shared |
| Side effects | Changing a nested value affects both | Changes never affect the original |
| Speed | Faster, less memory | Slower, more memory |
| Cyclic references | Not an issue | Must be handled (Python does this) |
| Python | copy.copy(), a[:], a.copy() | copy.deepcopy() |
| Java | Default Object.clone() | Manual: clone each field or serialize |
| Best when | Flat data, or sharing nested data is fine | Nested mutable data that must stay separate |
Code Examples (Python & Java)
The clearest demo uses a list that contains other lists. The change to a nested list reveals the difference.
Python — copy.copy() vs copy.deepcopy():
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original) # new outer list, SAME inner lists
deep = copy.deepcopy(original) # new outer list AND new inner lists
original[0][0] = 99
print(shallow) # [[99, 2], [3, 4]] -> inner list is shared, so it changed
print(deep) # [[1, 2], [3, 4]] -> fully independent, unaffectedThe shallow copy shares the inner lists, so editing original[0][0] also changes shallow. The deep copy rebuilt the inner lists, so it stays the same.
Java — the default clone() is shallow:
class Address { String city; }
class Person implements Cloneable {
String name;
Address address; // nested object
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person) super.clone(); // SHALLOW: shares the Address
}
}Here super.clone() copies the address reference, so the clone and the original share one Address. For a deep copy you also clone the nested Address, for example copy.address = new Address(this.address). The same idea applies in C++ and C#, where the default member-wise copy is shallow and a custom copy constructor makes it deep.
When to Use Shallow Copy vs Deep Copy
Use a shallow copy when the object is flat, or when sharing the nested data is exactly what you want. It is faster and uses less memory, so it suits large structures whose inner parts will not be changed independently.
Use a deep copy when nested mutable objects must stay separate. If you copy an object, edit the copy, and the original must not change, you need a deep copy. Configuration snapshots, undo history, and safe defaults all rely on it.
One rule covers most cases. If every value is immutable, such as numbers, strings, or tuples of those, shallow and deep behave the same. The moment a nested mutable object appears, the choice starts to matter.
Interview Questions on Shallow Copy vs Deep Copy
Object.clone() performs a shallow copy. It copies each field value, so primitive fields are duplicated but object fields keep the same reference. To make a deep copy, you override clone() to also clone the nested objects, or you use a copy constructor or serialization.copy.deepcopy(obj) from the copy module. It recursively copies the object and every nested object, and it tracks already-copied objects so cyclic references do not cause infinite loops. copy.copy(obj), by contrast, makes a shallow copy that shares the nested objects.Frequently Asked Questions
b = a copies no object at all; both names refer to the same single object. A shallow copy creates a new outer object that merely shares the nested objects. So assignment shares everything, while a shallow copy shares only what is nested.copy module. Use copy.copy(obj) for a shallow copy and copy.deepcopy(obj) for a deep copy. For a simple list, a[:], list(a), and a.copy() also make a shallow copy, while only copy.deepcopy() duplicates nested lists.Wrapping Up
The whole comparison comes down to nested objects. A shallow copy duplicates the outer object but shares what is inside, while a deep copy duplicates everything and shares nothing. Until a nested mutable object appears, the two look identical.
So ask one question before you copy: will I change the nested data and expect the original to stay the same? If yes, reach for a deep copy. If the data is flat or sharing is fine, a shallow copy is faster and simpler.
Related reading on DiffStudy:
- Stack vs Heap Memory Allocation
- Comparator vs Comparable in Java
- Procedural vs Object-Oriented Programming
- Structure vs Class in C
- CS Fundamentals hub