By WebNest Studio Engineering Team
Java - Core Tutorial
Object Cloning
Object cloning creates a new object that is a copy of an existing one, rather than a new reference to the same object. Java supports this through the clone() method inherited from Object, combined with the Cloneable marker interface, though this mechanism is widely considered one of the more awkward corners of the language.
Why it matters
Copying an object sounds simple until the object contains another object. A shallow copy shares the inner data, so a change made through the copy also changes the original — a bug that appears far from where it was caused. Knowing the difference between shallow and deep copies matters for any code that hands objects to other parts of a system.
Watch: Object Cloning in Java | Shallow Copy vs Deep Copy | Core Java Tutorial
Explains what object cloning is, how the clone() method and the Cloneable marker interface work together, and the difference between a shallow copy and a deep copy, with worked examples and common interview questions.
What Is Object Cloning?
Writing Student b = a; does not copy anything. It copies the reference, so a and b are two names for the same object and a change made through one is seen through the other. Cloning creates a second, separate object on the heap whose fields start with the same values as the original. After cloning, original == copy is false because they are different objects. Whether they still share anything inside depends on whether the copy is shallow or deep.
Understanding the clone() Method
clone() is declared in java.lang.Object as protected native Object clone() throws CloneNotSupportedException. Because it is protected, outside code cannot call it on your objects until you override it. A typical override does three things.
- Make it
publicso other classes can call it. - Call
super.clone(), which allocates the new object and copies every field. No constructor runs during this copy. - Use a covariant return type (
public Student clone()instead ofObject) so callers do not need a cast. The cast fromsuper.clone()happens once, inside the method.
The Cloneable Interface
Cloneable is a marker interface: it declares no methods at all. Its only job is to tell Object.clone() that cloning is permitted for this class. If a class calls super.clone() without implementing Cloneable, the call throws CloneNotSupportedException at runtime. Arrays are the exception: every array is cloneable, and array.clone() is public and throws no checked exception, which makes it the easiest way to copy an array field.
Shallow Cloning
The default Object.clone() performs a shallow copy: it copies each field's value directly. For primitive fields, this copies the actual value. For reference fields (like an array or another object), it copies only the reference itself — so the clone and the original end up pointing at the same underlying object. Modifying that shared object through one reference is visible through the other. Immutable fields such as String are safe to share, because nobody can change them.
Deep Cloning
A deep copy duplicates not just the top-level object but every mutable object it references, recursively, so the clone shares no mutable state with the original. Java does not do this automatically; you must implement it yourself, typically by calling super.clone() for the shallow copy and then replacing each mutable field with its own copy (marks.clone(), address.clone(), new ArrayList<>(list)). Every class in the chain that needs copying must support it as well.
Shallow Copy vs Deep Copy
- Top-level object: both create a new object, so
original == copyisfalsein both cases. - Primitive and immutable fields: copied by value in both; no difference.
- Mutable reference fields: a shallow copy shares them with the original; a deep copy gets its own copies.
- Effort: a shallow copy is one call to
super.clone(); a deep copy needs extra code for every mutable field. - Speed and memory: a shallow copy is cheaper; a deep copy allocates more objects.
- When to use: shallow is fine when the fields are immutable or meant to be shared; use deep when the copy must be changed without affecting the original.
Pitfalls and Alternatives to clone()
The clone mechanism has well-known design problems: a marker interface that silently changes how another method behaves, a checked exception that callers must handle, a cast, no constructor call, and final fields that cannot be reassigned inside clone(), which makes deep copies of them impossible. That is why many modern Java codebases avoid clone() entirely and use a copy constructor (new Cart(otherCart)) or a static factory method (Cart.copyOf(otherCart)) instead. Both are ordinary Java code, work with final fields, and make it obvious which fields are copied deeply.
Common Interview Questions
- What is object cloning in Java? Creating a new object that is a field-by-field copy of an existing one, usually through
clone(). - Where is clone() defined and why is it protected? In
java.lang.Object. It is protected so a class has to opt in by overriding it as public. - What is a marker interface? Give an example. An interface with no methods that tags a class with a capability, such as
CloneableorSerializable. - What happens if you call super.clone() without implementing Cloneable? It throws
CloneNotSupportedExceptionat runtime. - Does clone() call the constructor? No. The new object is created by copying memory, so constructor code is skipped.
- Shallow copy vs deep copy? A shallow copy shares the objects that fields refer to; a deep copy also copies those objects.
- How do you make a deep copy? Call
super.clone(), then replace each mutable field with its own copy, or use a copy constructor that does the same.
Examples
Shallow clone sharing a mutable array field between original and copy
import java.util.Arrays;
class Team implements Cloneable {
String name;
int[] scores;
Team(String name, int[] scores) {
this.name = name;
this.scores = scores;
}
@Override
public Team clone() throws CloneNotSupportedException {
return (Team) super.clone(); // shallow copy
}
}
public class CloningDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Team original = new Team("Alpha", new int[]{10, 20, 30});
Team copy = original.clone();
copy.name = "Beta"; // independent, primitive-like String reassignment
copy.scores[0] = 999; // mutates the shared array!
System.out.println(original.name + " " + Arrays.toString(original.scores));
System.out.println(copy.name + " " + Arrays.toString(copy.scores));
}
}
Alpha [999, 20, 30]
Beta [999, 20, 30]
Deep clone: super.clone() first, then copy the array and the nested Address
class Address implements Cloneable {
String city;
Address(String city) {
this.city = city;
}
@Override
public Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
}
class Student implements Cloneable {
String name;
int[] marks;
Address address;
Student(String name, int[] marks, Address address) {
this.name = name;
this.marks = marks;
this.address = address;
}
@Override
public Student clone() throws CloneNotSupportedException {
Student copy = (Student) super.clone(); // shallow copy first
copy.marks = marks.clone(); // then copy each mutable field
copy.address = address.clone();
return copy;
}
}
public class DeepCloneDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Student original = new Student("Riya", new int[] {80, 90}, new Address("Delhi"));
Student copy = original.clone();
copy.marks[0] = 50;
copy.address.city = "Mumbai";
System.out.println(original == copy);
System.out.println(original.marks[0] + " " + original.address.city);
System.out.println(copy.marks[0] + " " + copy.address.city);
}
}
false
80 Delhi
50 Mumbai
A copy constructor as the alternative to clone(), compared with a plain reference copy
import java.util.ArrayList;
import java.util.List;
class Cart {
private final List<String> items;
Cart(List<String> items) {
this.items = new ArrayList<>(items);
}
Cart(Cart other) { // copy constructor
this(other.items);
}
void add(String item) {
items.add(item);
}
List<String> items() {
return items;
}
}
public class CopyConstructorDemo {
public static void main(String[] args) {
Cart original = new Cart(List.of("Pen", "Book"));
Cart copy = new Cart(original);
copy.add("Bag");
System.out.println(original.items());
System.out.println(copy.items());
Cart sameCart = original; // reference copy, not a clone
sameCart.add("Ink");
System.out.println(original.items());
}
}
[Pen, Book]
[Pen, Book, Bag]
[Pen, Book, Ink]
Common Mistakes
- Thinking
Student b = a;makes a copy — it only copies the reference, so both names point to the same object. - Calling clone() on a class that does not implement Cloneable and being surprised by a CloneNotSupportedException at runtime.
- Assuming clone() always produces a fully independent copy — the default is shallow, so shared mutable fields still alias each other.
- Forgetting to manually deep-copy mutable reference fields (arrays, lists, custom objects) inside an overridden clone() when independence is required.
- Reaching for clone() at all in new code instead of a copy constructor or a static factory method, which most modern style guides prefer.
Exercise
The copy constructor below copies the reference to the ratings array instead of the array itself, so changing the copy also changes the original and the program prints [1, 3, 4] twice. Make it a deep copy.
Expected output: [5, 3, 4] then [1, 3, 4]
import java.util.Arrays;
class Playlist {
int[] ratings;
Playlist(int[] ratings) {
this.ratings = ratings;
}
Playlist(Playlist other) {
this.ratings = other.ratings;
}
}
public class CopyDemo {
public static void main(String[] args) {
Playlist original = new Playlist(new int[] {5, 3, 4});
Playlist copy = new Playlist(original);
copy.ratings[0] = 1;
System.out.println(Arrays.toString(original.ratings));
System.out.println(Arrays.toString(copy.ratings));
}
}
Show hints
- Both objects currently hold a reference to the same array.
Arrays.copyOf(other.ratings, other.ratings.length)creates a separate array with the same contents.
Show solution
import java.util.Arrays;
class Playlist {
int[] ratings;
Playlist(int[] ratings) {
this.ratings = ratings;
}
Playlist(Playlist other) {
this.ratings = Arrays.copyOf(other.ratings, other.ratings.length);
}
}
public class CopyDemo {
public static void main(String[] args) {
Playlist original = new Playlist(new int[] {5, 3, 4});
Playlist copy = new Playlist(original);
copy.ratings[0] = 1;
System.out.println(Arrays.toString(original.ratings)); // [5, 3, 4]
System.out.println(Arrays.toString(copy.ratings)); // [1, 3, 4]
}
}
Quick quiz
1. A class overrides clone() and calls super.clone() but does not implement Cloneable. What happens when it is cloned?
- A. It throws CloneNotSupportedException
- B. It returns a shallow copy
- C. It returns null
- D. It does not compile
Show answer
A. Object.clone() checks at runtime that the class implements Cloneable and throws CloneNotSupportedException if it does not.
2. What kind of copy does Object.clone() make by default?
- A. A deep copy
- B. A shallow copy
- C. No copy; it returns the same object
- D. A copy of primitive fields only
Show answer
B. It copies every field as it is. Reference fields are copied as references, so the clone shares those objects with the original.
3. How many methods does the Cloneable interface declare?
- A. One: clone()
- B. Two
- C. None; it is a marker interface
- D. It depends on the Java version
Show answer
C. Cloneable is empty. It only signals to Object.clone() that copying this class is permitted.
Interview questions
What is the difference between a shallow copy and a deep copy?
Show answer
A shallow copy creates a new object but copies its fields directly, so any field that refers to another object ends up pointing at the same object as the original. A deep copy also copies the objects those fields refer to, recursively, so the two copies share nothing mutable and can be changed independently.
What are the alternatives to clone()?
Show answer
A copy constructor, which takes an object of the same class and copies its fields, or a static factory method such as copyOf. Both are clearer than clone(): they need no marker interface, no cast, no checked exception, and they make it explicit which fields are copied deeply. Most Java style guides recommend them over Cloneable.
Key Points to Remember
- Assigning one variable to another copies the reference; cloning creates a separate object.
- Override clone() as public, call super.clone(), and use a covariant return type to avoid casts at the call site.
- Shallow cloning copies field values, but reference fields still point to the same shared objects.
- Deep cloning requires manually copying every mutable referenced object as well.
- Cloneable is a marker interface with no methods; without it, clone() throws CloneNotSupportedException.
- Copy constructors or static factory methods are a commonly preferred alternative to clone().
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering