深入理解Java拷贝
浅拷贝(Shallow Copy)
浅拷贝会创建一个对象,对于原始对象(被拷贝的对象)中的基本数据类型字段,浅拷贝会直接复制这些值,对于引用类型的字段,浅拷贝会复制这个引用类型字段的地址,而不是复制这个引用类型字段所指向的对象本身。这意味着,如果原始对象中的引用类型字段指向一个对象,那么浅拷贝后的新对象中的相同字段仍然指向同一个对象。
这种情况下,我们如果修改了新对象中的引用类型字段所指向的对象,那么原始对象中的相同字段也会受到影响,因为它们指向的是同一个对象。
举个例子:
package org.example;
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("Beijing");
Person p1 = new Person("Alice", addr);
Person p2 = (Person) p1.clone(); // 浅拷贝
p2.setName("John");
p2.getAddress().setCity("Shanghai"); // 修改 p2 的 address
System.out.println("p1: " + p1);
System.out.println("p2: " + p2);
}
}
class Address implements Cloneable {
private String city;
public Address(String city) { this.city = city; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
@Override
public String toString() { return "Address{city='" + city + "'}"; }
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝
}
}
class Person implements Cloneable {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name; this.address = address;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
@Override
public String toString() {
return "Person{name='" + name + "', address=" + address + '}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 只做浅拷贝,address 仍然指向同一个对象
}
}
深拷贝(Deep Copy)
深拷贝会拷贝所有的属性,包括基本类型和引用类型。对于引用类型的字段,深拷贝会创建一个新的对象,并将原始对象中的引用类型字段所指向的对象也进行拷贝。这意味着,深拷贝后的新对象中的引用类型字段指向的是一个全新的对象,而不是原始对象中的同一个对象。
(1)实现Cloneable并重写clone()方法
对于简单对象,可以让类实现Cloneable接口,并重写clone()方法,在其中手动递归地克隆引用类型字段:
package org.example;
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("Beijing");
Person p1 = new Person("Alice", addr);
Person p2 = (Person) p1.clone(); // 浅拷贝
p2.setName("John");
p2.getAddress().setCity("Shanghai"); // 修改 p2 的 address
System.out.println("p1: " + p1);
System.out.println("p2: " + p2);
}
}
class Address implements Cloneable {
private String city;
public Address(String city) { this.city = city; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
@Override
public String toString() { return "Address{city='" + city + "'}"; }
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝
}
}
class Person implements Cloneable {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name; this.address = address;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
@Override
public String toString() {
return "Person{name='" + name + "', address=" + address + '}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person copy = (Person) super.clone();
copy.address = (Address) this.address.clone();
return copy; // 深拷贝
}
}
(2)使用序列化和反序列化
package org.example;
import java.io.*;
public class Main {
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Address addr = new Address("Beijing");
Person p1 = new Person("Alice", addr);
Person p2 = deepCopy(p1);
p2.setName("John");
p2.getAddress().setCity("Shanghai"); // 修改 p2 的 address
System.out.println("p1: " + p1);
System.out.println("p2: " + p2);
}
public static <T extends Serializable> T deepCopy(T obj) throws IOException, ClassNotFoundException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (T) ois.readObject();
}
}
class Address implements Serializable{
private String city;
public Address(String city) { this.city = city; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
@Override
public String toString() { return "Address{city='" + city + "'}"; }
}
class Person implements Serializable{
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name; this.address = address;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
@Override
public String toString() {
return "Person{name='" + name + "', address=" + address + '}';
}
}
注意点
对于集合类型的深拷贝,需要特别注意,因为集合中的元素可能是引用类型的对象。可以使用递归的方式来实现深拷贝,或者使用第三方库如Apache Commons Lang的SerializationUtils来简化操作。