Introduction
Cloning creates an exact copy of an object. Java provides clone() method in Object class for creating object copies.
Why Clone?
- Preserve original - modify copy without affecting original
- Backup - keep original safe
- Testing - test with copy
- Performance - faster than creating from scratch
Cloneable Interface
Marker interface (no methods) that indicates object can be cloned.
class MyClass implements Cloneable {
// Class can be cloned
}
Without Cloneable:
clone()throws CloneNotSupportedException
Basic clone() Method
class Student implements Cloneable {
String name;
int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
public class Main {
public static void main(String[] args) {
try {
Student s1 = new Student("John", 20);
Student s2 = (Student) s1.clone();
System.out.println("Original: " + s1); // John (20)
System.out.println("Clone: " + s2); // John (20)
// Modify clone
s2.name = "Alice";
s2.age = 22;
System.out.println("\nAfter modifying clone:");
System.out.println("Original: " + s1); // John (20)
System.out.println("Clone: " + s2); // Alice (22)
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
Shallow Copy vs Deep Copy
Shallow Copy:
- Copies object and references
- Doesn’t copy referenced objects
- Default clone() behavior
Deep Copy:
- Copies object and all referenced objects
- Creates independent copy
- Must implement manually
Shallow Copy Example
class Address {
String city;
Address(String city) {
this.city = city;
}
}
class Person implements Cloneable {
String name;
Address address; // Reference type
Person(String name, Address address) {
this.name = name;
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // Shallow copy
}
}
public class Main {
public static void main(String[] args) {
try {
Address addr = new Address("New York");
Person p1 = new Person("John", addr);
Person p2 = (Person) p1.clone();
System.out.println("p1: " + p1.name + ", " + p1.address.city);
System.out.println("p2: " + p2.name + ", " + p2.address.city);
// Modify clone's address
p2.address.city = "Boston";
// Original also changed (shallow copy problem!)
System.out.println("\nAfter modifying clone:");
System.out.println("p1: " + p1.name + ", " + p1.address.city); // Boston!
System.out.println("p2: " + p2.name + ", " + p2.address.city); // Boston
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
Deep Copy Example
class Address implements Cloneable {
String city;
String country;
Address(String city, String country) {
this.city = city;
this.country = country;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
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 {
// Deep copy - clone referenced objects too
Person cloned = (Person) super.clone();
cloned.address = (Address) address.clone();
return cloned;
}
}
public class Main {
public static void main(String[] args) {
try {
Address addr = new Address("New York", "USA");
Person p1 = new Person("John", addr);
Person p2 = (Person) p1.clone();
System.out.println("p1: " + p1.name + ", " + p1.address.city);
System.out.println("p2: " + p2.name + ", " + p2.address.city);
// Modify clone's address
p2.address.city = "Boston";
// Original unchanged (deep copy works!)
System.out.println("\nAfter modifying clone:");
System.out.println("p1: " + p1.name + ", " + p1.address.city); // New York
System.out.println("p2: " + p2.name + ", " + p2.address.city); // Boston
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
Deep Copy with Array
class Student implements Cloneable {
String name;
int[] marks; // Array reference
Student(String name, int[] marks) {
this.name = name;
this.marks = marks;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student cloned = (Student) super.clone();
// Deep copy the array
cloned.marks = marks.clone();
return cloned;
}
void displayMarks() {
System.out.print(name + ": ");
for (int mark : marks) {
System.out.print(mark + " ");
}
System.out.println();
}
}
public class Main {
public static void main(String[] args) {
try {
int[] marks = {85, 90, 78};
Student s1 = new Student("John", marks);
Student s2 = (Student) s1.clone();
s1.displayMarks(); // John: 85 90 78
s2.displayMarks(); // John: 85 90 78
// Modify clone's marks
s2.marks[0] = 95;
System.out.println("\nAfter modifying clone:");
s1.displayMarks(); // John: 85 90 78 (unchanged)
s2.displayMarks(); // John: 95 90 78
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
Copy Constructor Alternative
class Student {
String name;
int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
// Copy constructor
Student(Student other) {
this.name = other.name;
this.age = other.age;
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
public class Main {
public static void main(String[] args) {
Student s1 = new Student("John", 20);
Student s2 = new Student(s1); // Copy using constructor
System.out.println("Original: " + s1);
System.out.println("Copy: " + s2);
s2.name = "Alice";
System.out.println("\nAfter modifying copy:");
System.out.println("Original: " + s1); // John (20)
System.out.println("Copy: " + s2); // Alice (20)
}
}
Complete Example: Employee Cloning
class Department implements Cloneable {
String name;
String location;
Department(String name, String location) {
this.name = name;
this.location = location;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return name + " (" + location + ")";
}
}
class Employee implements Cloneable {
int id;
String name;
double salary;
Department department;
String[] skills;
Employee(int id, String name, double salary, Department department, String[] skills) {
this.id = id;
this.name = name;
this.salary = salary;
this.department = department;
this.skills = skills;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// Shallow copy first
Employee cloned = (Employee) super.clone();
// Deep copy reference types
cloned.department = (Department) department.clone();
cloned.skills = skills.clone();
return cloned;
}
void display() {
System.out.println("ID: " + id);
System.out.println("Name: " + name);
System.out.println("Salary: $" + salary);
System.out.println("Department: " + department);
System.out.print("Skills: ");
for (String skill : skills) {
System.out.print(skill + " ");
}
System.out.println("\n");
}
}
public class Main {
public static void main(String[] args) {
try {
Department dept = new Department("IT", "Building A");
String[] skills = {"Java", "Python", "SQL"};
Employee e1 = new Employee(101, "John", 50000, dept, skills);
Employee e2 = (Employee) e1.clone();
System.out.println("Original Employee:");
e1.display();
System.out.println("Cloned Employee:");
e2.display();
// Modify clone
e2.id = 102;
e2.name = "Alice";
e2.salary = 55000;
e2.department.name = "HR";
e2.skills[0] = "JavaScript";
System.out.println("\nAfter Modifying Clone:");
System.out.println("Original Employee:");
e1.display();
System.out.println("Cloned Employee:");
e2.display();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
CloneNotSupportedException
class MyClass { // Not implementing Cloneable
String name;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // Will throw exception
}
}
public class Main {
public static void main(String[] args) {
try {
MyClass obj1 = new MyClass();
MyClass obj2 = (MyClass) obj1.clone();
} catch (CloneNotSupportedException e) {
System.out.println("Clone not supported!");
e.printStackTrace();
}
}
}
clone() Method Rules
- Implement Cloneable interface
- Override clone() method
- Call super.clone()
- Cast return type
- Handle exception
- Deep copy if needed
Comparison: Shallow vs Deep
// Shallow Copy
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // Only copies references
}
// Deep Copy
@Override
protected Object clone() throws CloneNotSupportedException {
MyClass cloned = (MyClass) super.clone();
cloned.reference = (Type) this.reference.clone(); // Copy objects
cloned.array = this.array.clone(); // Copy arrays
return cloned;
}
Serialization Alternative
Deep copy using serialization:
import java.io.*;
class Student implements Serializable {
String name;
int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
// Deep copy using serialization
Student deepCopy() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Student) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
When to Use Clone?
Use clone():
- Simple objects
- Performance critical
- Object graph known
Avoid clone():
- Complex inheritance
- Many mutable fields
- Consider copy constructor instead
Best Practices
- Implement Cloneable explicitly
- Override clone() as public
- Document shallow vs deep
- Consider alternatives (copy constructor)
- Test thoroughly
- Handle exceptions properly
Quick Reference
// Basic cloning
class MyClass implements Cloneable {
// Fields
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // Shallow copy
}
}
// Deep cloning
class MyClass implements Cloneable {
ReferenceType ref;
int[] array;
@Override
public Object clone() throws CloneNotSupportedException {
MyClass cloned = (MyClass) super.clone();
cloned.ref = (ReferenceType) this.ref.clone();
cloned.array = this.array.clone();
return cloned;
}
}
// Usage
try {
MyClass original = new MyClass();
MyClass copy = (MyClass) original.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
Common Mistakes
// ✗ Not implementing Cloneable
class MyClass {
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // Exception!
}
}
// ✗ Shallow copy for mutable objects
class Person implements Cloneable {
Address address; // Mutable reference
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // Problem: shared address!
}
}
// ✓ Correct - deep copy
class Person implements Cloneable {
Address address;
protected Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
cloned.address = (Address) this.address.clone();
return cloned;
}
}
Exam Tips
Remember:
- clone() creates object copy
- Cloneable marker interface required
- Shallow copy - copies references
- Deep copy - copies objects
- super.clone() for cloning
- CloneNotSupportedException if not Cloneable
- Arrays need clone() for deep copy
- Override as public
- Cast return type
- Copy constructor alternative
Common Questions:
- What is cloning?
- Shallow vs deep copy?
- How to implement clone()?
- What is Cloneable interface?
- When CloneNotSupportedException?
- How to deep copy arrays?
- Clone() vs copy constructor?
- When to use cloning?
- How to clone reference types?
- Best practices for cloning?