目录
一、概念
二、实现方式
2.1 浅拷贝(不推荐)
2.2 深拷贝
2.2.1 方法一:重写 clone() 方法并递归克隆(常用)
2.2.2 方法二:通过序列化实现(更强大,但更重)
2.2.3 使用拷贝构造方法或拷贝工厂(推荐)
一、概念
浅拷贝 (Shallow Copy):只复制对象本身以及对象中的基本数据类型字段的值,而对于对象中的引用类型字段,则只复制其内存地址(即引用),而不复制引用的对象本身。
影响:原始对象和拷贝对象中的引用字段指向的是同一个堆内存中的子对象。修改其中一个对象的引用字段的内容,另一个对象的对应字段也会“看到”这个变化。
深拷贝 (Deep Copy):仅复制对象本身和基本数据类型字段的值,还会递归地复制所有引用类型字段所指向的对象,直到所有可达的对象都被复制。
影响:原始对象和拷贝对象完全独立,它们所有的引用字段都指向不同的对象。修改其中一个对象的任何内容,都不会影响另一个对象。
二、实现方式
2.1 浅拷贝(不推荐)
实现方式:依靠Object
类的 clone()
方法。
实现条件:
-
被拷贝的类实现
Cloneable
接口(这是一个标记接口,没有方法)。 -
被拷贝的类重写
Object
的clone()
方法,并在其中调用super.clone()
。
public class Person implements Cloneable {private String name; // String (不可变对象,可视为基本类型)private int age; // 基本类型private Address address; // 引用类型// 构造方法、getters、setters 省略...@Overridepublic Object clone() throws CloneNotSupportedException {// 直接调用Object的clone()方法,完成基本数据类型和引用地址的复制return super.clone();}
}public class Address {private String city;private String street;// 构造方法、getters、setters...
}public class TestShallowCopy {public static void main(String[] args) throws CloneNotSupportedException {Address addr = new Address("北京", "长安街");Person original = new Person("张三", 25, addr);// 进行浅拷贝Person copied = (Person) original.clone();System.out.println(original == copied); // false,是两个不同的对象System.out.println(original.getName() == copied.getName()); // true,String池或同一对象(可能)System.out.println(original.getAddress() == copied.getAddress()); // true!关键在这里:引用指向同一个Address对象// 修改拷贝对象的引用类型成员copied.getAddress().setCity("上海");// 原始对象的address也被修改了!System.out.println(original.getAddress().getCity()); // 输出:上海}
}
2.2 深拷贝
2.2.1 方法一:重写 clone()
方法并递归克隆(常用)
// 1、让 Address 类也变得可克隆(实现 Cloneable 并重写 clone())
public class Address implements Cloneable {private String city;private String street;// ... 其他代码 ...@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}// 2、在 Person 的 clone() 方法中,不仅调用 super.clone(),还要手动克隆 address 字段
public class Person implements Cloneable {private String name;private int age;private Address address;// ... 其他代码 ...@Overridepublic Object clone() throws CloneNotSupportedException {// 1. 先调用super.clone()完成浅拷贝Person copied = (Person) super.clone();// 2. 对引用类型字段,手动调用其clone()方法进行深拷贝copied.address = (Address) this.address.clone();// 3. 返回深拷贝后的对象return copied;}
}// 3、测试
public class TestDeepCopy {public static void main(String[] args) throws CloneNotSupportedException {Address addr = new Address("北京", "长安街");Person original = new Person("张三", 25, addr);Person copied = (Person) original.clone();System.out.println(original.getAddress() == copied.getAddress()); // false!现在是不同的Address对象// 修改拷贝对象的addresscopied.getAddress().setCity("上海");// 原始对象的address不受影响System.out.println(original.getAddress().getCity()); // 输出:北京System.out.println(copied.getAddress().getCity()); // 输出:上海}
}
2.2.2 方法二:通过序列化实现(更强大,但更重)
前提:所有涉及到的类都必须实现 java.io.Serializable
接口。
import java.io.*;public class DeepCopyUtil {@SuppressWarnings("unchecked")public static <T extends Serializable> T deepCopy(T object) {try (ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos)) {// 将对象写入字节流oos.writeObject(object);oos.flush();try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais)) {// 从字节流中读出新的对象return (T) ois.readObject();}} catch (IOException | ClassNotFoundException e) {throw new RuntimeException("Deep copy failed", e);}}
}// Person和Address类都需要实现Serializable接口
public class Person implements Serializable { ... }
public class Address implements Serializable { ... }// 使用工具类进行深拷贝
Person original = new Person(...);
Person copied = DeepCopyUtil.deepCopy(original); // 完美的深拷贝
优点:无需关心对象内部复杂的引用结构,序列化机制会自动完成所有递归复制。
缺点:性能开销比 clone()
方法大;所有相关类都必须实现 Serializable
接口。
2.2.3 使用拷贝构造方法或拷贝工厂(推荐)
这是一种非常推荐的方式,它不依赖 Cloneable
接口,代码更清晰,也更灵活。
-
拷贝构造方法:接受一个同一类型的参数,并据此构造一个新对象。
-
拷贝工厂:一个静态方法,用于完成拷贝。
public class Person {private String name;private int age;private Address address;// 拷贝构造方法public Person(Person other) {this.name = other.name;this.age = other.age;// 对引用类型,调用其拷贝构造方法进行深拷贝this.address = new Address(other.address); // 假设Address也有拷贝构造方法}// 拷贝工厂 (静态方法)public static Person newInstance(Person other) {return new Person(other);}
}public class Address {private String city;private String street;// Address的拷贝构造方法public Address(Address other) {this.city = other.city;this.street = other.street;}
}// 使用
Person original = new Person(...);
Person copied = new Person(original); // 使用拷贝构造方法
// 或
Person copied2 = Person.newInstance(original); // 使用拷贝工厂
优点:代码意图明确,易于控制和扩展,是《Effective Java》推荐的方式。