Copy Constructor in Java – Explained with Examples
A copy constructor creates a new object as a copy of an existing object of the same class: Point copy = new Point(original);. Java does not generate copy constructors automatically, as C++ does, but writing one is the simplest and clearest way to duplicate an object in Java.
This tutorial shows how to write copy constructors, explains the crucial difference between shallow and deep copies, introduces defensive copying, and compares copy constructors with clone().
1. Why Copy Objects?
In Java, assigning one variable to another (b = a) copies only the reference: both variables point to the same object, and a change through one is visible through the other. When you need an independent object, for example to modify a copy while keeping the original, or to protect your class's data, you need a real copy.
2. Writing a Copy Constructor
A copy constructor takes one parameter of the same class and copies its fields:
public class CopyConstructorBasics {
static class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
Point(Point other) { // copy constructor
this.x = other.x;
this.y = other.y;
}
@Override public String toString() { return "(" + x + ", " + y + ")"; }
}
public static void main(String[] args) {
Point original = new Point(3, 4);
Point copy = new Point(original); // a new, independent object
Point alias = original; // NOT a copy: same object
copy.x = 100;
alias.y = 50;
System.out.println("original = " + original);
System.out.println("copy = " + copy);
System.out.println("original == copy: " + (original == copy));
}
}
original = (3, 50) copy = (100, 4) original == copy: false
Changing copy did not affect original, but changing alias did, because alias is just another name for the same object.
3. Shallow Copy vs Deep Copy
Copying fields works perfectly for primitives and immutable objects such as String. But if a field refers to a mutable object, such as a list or an array, copying the reference means both objects share it. That is a shallow copy. A deep copy also copies those mutable objects:
import java.util.ArrayList;
import java.util.List;
public class ShallowVsDeep {
static class Playlist {
String name;
List<String> songs;
Playlist(String name, List<String> songs) {
this.name = name;
this.songs = songs;
}
// Shallow copy: the list itself is shared
static Playlist shallowCopy(Playlist p) {
return new Playlist(p.name, p.songs);
}
// Copy constructor doing a deep copy: a new list with the same songs
Playlist(Playlist other) {
this.name = other.name; // String is immutable: sharing is safe
this.songs = new ArrayList<>(other.songs); // mutable list: copy it
}
}
public static void main(String[] args) {
Playlist original = new Playlist("Road trip", new ArrayList<>(List.of("Song A", "Song B")));
Playlist shallow = Playlist.shallowCopy(original);
Playlist deep = new Playlist(original);
original.songs.add("Song C");
System.out.println("original: " + original.songs);
System.out.println("shallow: " + shallow.songs + " <- changed too (shared list)");
System.out.println("deep: " + deep.songs + " <- independent");
}
}
original: [Song A, Song B, Song C] shallow: [Song A, Song B, Song C] <- changed too (shared list) deep: [Song A, Song B] <- independent
| Field type | What the copy constructor should do |
|---|---|
Primitives (int, double…) | Copy the value |
Immutable objects (String, Integer, records, LocalDate) | Copy the reference; sharing is safe |
| Mutable collections | Create a new collection: new ArrayList<>(other.list) |
| Arrays | other.array.clone() or Arrays.copyOf() |
| Your own mutable objects | Call their copy constructors |
4. Defensive Copies
A class that stores a mutable object passed in by a caller, or returns one from a getter, can be modified from outside without noticing. Defensive copies on the way in and out protect the object's state:
import java.util.Arrays;
public class DefensiveCopy {
static final class Schedule {
private final int[] days;
Schedule(int[] days) {
this.days = days.clone(); // copy in: caller cannot change our data later
}
Schedule(Schedule other) { // copy constructor
this(other.days);
}
int[] getDays() { return days.clone(); } // copy out: caller cannot change our data
}
public static void main(String[] args) {
int[] input = {1, 3, 5};
Schedule s = new Schedule(input);
input[0] = 99; // does not affect s
int[] fromGetter = s.getDays();
fromGetter[1] = 77; // does not affect s either
System.out.println("schedule days: " + Arrays.toString(s.getDays()));
System.out.println("copy of schedule: " + Arrays.toString(new Schedule(s).getDays()));
}
}
schedule days: [1, 3, 5] copy of schedule: [1, 3, 5]
The caller changed both the original input array and the array returned by the getter, but the schedule stayed the same. The copy constructor reuses the main constructor with this(other.days), which already makes the copy.
5. Copy Constructor vs clone()
| Feature | Copy constructor | clone() |
|---|---|---|
| Interface needed | None | Cloneable |
| Checked exception | No | CloneNotSupportedException must be handled |
| Return type | The exact class, no cast | Object unless overridden covariantly |
Works with final fields | Yes | Hard: fields are copied before your code runs |
| Control over deep copying | Full and explicit | Must override and fix up fields manually |
| Recommended | Yes (also static factories like copyOf()) | Mostly for arrays |
Joshua Bloch's Effective Java recommends copy constructors or copy factories over clone(). For arrays, clone() is still the simplest choice. See Object Cloning in Java.
6. When You Do Not Need Copies
Immutable objects never need copying, because nobody can change them. And the JDK collections already provide copy constructors and copy factories:
import java.util.ArrayList;
import java.util.List;
public class CopyAlternatives {
record Address(String city, String pin) { }
public static void main(String[] args) {
// Immutable objects such as records never need copying: share them freely
Address home = new Address("Pune", "411001");
Address same = home;
System.out.println("records can be shared: " + same);
// Collections have copy constructors and copy factories
List<String> names = new ArrayList<>(List.of("Asha", "Ravi"));
List<String> mutableCopy = new ArrayList<>(names); // copy constructor
List<String> readOnlyCopy = List.copyOf(names); // immutable copy (Java 10+)
names.add("Meera");
System.out.println("names: " + names);
System.out.println("mutableCopy: " + mutableCopy);
System.out.println("readOnlyCopy: " + readOnlyCopy);
}
}
records can be shared: Address[city=Pune, pin=411001] names: [Asha, Ravi, Meera] mutableCopy: [Asha, Ravi] readOnlyCopy: [Asha, Ravi]
7. Copy Constructors and Inheritance
A subclass copy constructor should call the parent's copy constructor with super(other) and then copy its own fields. Note that a copy constructor always creates an object of the class it belongs to: new Animal(dog) creates an Animal, not a Dog. When the exact runtime type must be preserved, a polymorphic copy() method is the better tool.
8. Summary
- A copy constructor takes an object of the same class and creates an independent copy.
- Java does not create copy constructors automatically; you write them.
- Copy mutable fields (collections, arrays, mutable objects) to get a deep copy.
- Use defensive copies in constructors and getters to protect internal state.
- Prefer copy constructors or copy factories over
clone(), and immutable objects over copying.
Related Tutorials
Frequently Asked Questions (FAQ)
Q1. What is a copy constructor in Java?
A constructor that takes an object of the same class as its parameter and initialises the new object with copies of that object's fields.
Q2. Does Java provide a default copy constructor?
No. Unlike C++, Java never generates a copy constructor. You must write one if you need it.
Q3. What is the difference between a shallow and a deep copy?
A shallow copy copies field values, so referenced objects are shared. A deep copy also copies the mutable objects that fields refer to, so the two objects share nothing that can change.
Q4. Is assignment the same as copying?
No. b = a copies the reference, so both variables point to the same object. A copy constructor creates a new object.
Q5. Do I need to deep copy String fields?
No. Strings are immutable, so sharing them is safe. The same applies to wrapper classes, records with immutable components, and java.time types.
Q6. Copy constructor or clone(): which is better?
Usually a copy constructor. It needs no Cloneable interface, no checked exception, no cast, works with final fields and makes the copying logic explicit.
Q7. What is a defensive copy?
A copy made when storing or returning a mutable object, so that outside code cannot change your object's internal state through a shared reference.
Q8. How do I copy a list in Java?
new ArrayList<>(original) creates a modifiable shallow copy; List.copyOf(original) creates an unmodifiable one. Elements are shared in both cases.
Q9. How do I copy an array?
Use array.clone(), Arrays.copyOf(array, array.length) or System.arraycopy(). For 2D arrays, copy each row as well.
Q10. Can a copy constructor call another constructor?
Yes, with this(...): Point(Point p) { this(p.x, p.y); }. This reuses validation and defensive copying in the main constructor.
Q11. How do copy constructors work with inheritance?
The subclass copy constructor calls super(other) to copy inherited fields, then copies its own fields.
Q12. Does a copy constructor preserve the runtime type?
No. new Animal(dog) always creates an Animal. Use a polymorphic method such as copy() overridden in each subclass when the exact type matters.
Q13. What happens if the argument is null?
Accessing its fields throws NullPointerException. Validate with Objects.requireNonNull(other) for a clearer message.
Q14. Do records need copy constructors?
Usually not, because records with immutable components are themselves immutable and can be shared. To "change" a value, create a new record with the new value.
Q15. What is a copy factory method?
A static method that returns a copy, such as Point.copyOf(p) or List.copyOf(list). It can return cached or subclass instances and has a descriptive name.
Q16. Is a deep copy always necessary?
No. Copy only what can change and must stay independent. Deep copying immutable data wastes time and memory.
Q17. Can a copy constructor access private fields of the other object?
Yes. Access control in Java is per class, not per object, so code in a class can read the private fields of any object of that class.
Q18. How do I deep copy a list of mutable objects?
Copy each element too: original.stream().map(Item::new).collect(Collectors.toList()), assuming Item has a copy constructor.
Q19. Is serialization a way to deep copy?
It works by writing an object to bytes and reading it back, but it is slow, requires Serializable everywhere and has security risks. Explicit copy constructors are preferred.
Q20. Where are copy constructors used in the JDK?
Collections such as ArrayList, HashMap, TreeSet and LinkedList all have constructors that copy another collection, and String has String(String original).