在Java中,拷贝对象的方式可以分为浅拷贝和深拷贝两种。
浅拷贝
浅拷贝是指创建一个新对象,然后将原始对象的非静态字段值复制到新对象中,基本类型复制其值,引用类型则复制其引用(内存地址)。新旧对象仍然共享相同的引用对象,浅拷贝只复制对象本身,不会复制对象包含的引用对象。
可通过实现Cloneable接口并重写父类Object的clone方法来实现自定义的浅拷贝逻辑。Cloneable接口是一个标记接口,没有任何方法,仅用于表示该类支持clone操作。
class Address{
String city;
public Address(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
'}';
}
}
class Person implements Cloneable {
String name;
Integer age;
Address address;
public Person(String name, Integer age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class ShallowCopyTest {
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person("张三", 20, new Address("长沙"));
Person clone = (Person) person.clone();
// 修改克隆对象属性值
clone.address.city = "深圳"; // 修改引用对象属性值
clone.age = 18; // 修改基本类型值
System.out.println(person);
System.out.println(clone);
}
}
通过测试新旧对象是两个对象,但仍然共享相同的引用对象。
深拷贝
深拷贝是指创建一个新对象,并复制原始对象及其包含的所有引用对象,深拷贝会复制整个对象引用链,使得新旧对象完全独立,互不影响。
实现深拷贝可以通过以下两种方法:
A、自定义深拷贝逻辑:通过手动创建新对象或调用引用对象的clone方法,确保对象及其引用对象的完全复制。
@Override
protected Object clone() throws CloneNotSupportedException {
Person clone = (Person) super.clone();
clone.address = new Address(this.address.city); // 手动创建复制Address对象
return clone;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person clone = (Person) super.clone();
clone.address = (Address) this.address.clone(); // 调用引用对象的clone方法
return clone;
}
B、使用序列化和反序列化:将对象序列化为字节流后再反序列化,可以得到一个全新的对象,其中包含的引用对象也会被复制。进行序列化的对象及其引用对象需要实现Serializable接口。
public class ShallowCopyTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Person person = new Person("张三", 20, new Address("长沙"));
// 将对象写入字节流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(person);
// 从字节流中读取对象实现深拷贝
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
Person clone = (Person) ois.readObject();
// 修改克隆对象属性值
clone.address.city = "深圳"; // 修改引用对象属性值
clone.age = 18; // 修改基本类型值
System.out.println(person);
System.out.println(clone);
}
}
A和B方法都通过测试新旧对象是两个完全独立的对象。
需要注意在使用clone方法实现深拷贝时,被拷贝的类必须实现Cloneable接口并重写clone方法。而在使用序列化和反序列化实现深拷贝时,被拷贝的类及其引用对象都必须实现Serializable接口。