📅  最后修改于: 2023-12-03 14:43:00.767000             🧑  作者: Mango
Java克隆技术可以将一个Java对象的状态(即其所有的字段值)复制到另外一个Java对象中。在Java中,对象克隆使用clone()
方法,这个方法是从Object
类继承下来的:
public class Object {
protected native Object clone() throws CloneNotSupportedException;
}
为了使用clone()
方法,你的Java对象必须实现Cloneable
接口。该接口指定该对象允许克隆。如果尝试克隆一个未实现Cloneable
接口的Java对象,会引发CloneNotSupportedException
异常。
在Java中,对象克隆有两种方式,即"浅克隆"和"深克隆"。
浅克隆是指只克隆Java对象的字段值,并不克隆嵌套的对象。如果克隆一个对象,对于原始对象中的引用类型字段,它们的引用地址将被共享。
下面是一个例子:
class Person implements Cloneable {
public String name;
public int age;
public Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Address {
public String city;
public Address(String city) {
this.city = city;
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("Beijing");
Person p1 = new Person("Tom", 20, addr);
Person p2 = (Person) p1.clone();
System.out.println(p1.name + ", " + p1.age + ", " + p1.address.city);
System.out.println(p2.name + ", " + p2.age + ", " + p2.address.city);
addr.city = "Shanghai";
System.out.println(p1.address.city); //输出"Shanghai"
System.out.println(p2.address.city); //输出"Shanghai"
}
}
注意到Person
对象中包含了一个Address
对象。在clone()
方法中,我们调用了super.clone()
来克隆Person
对象。这意味着我们会获得一个新的Person
对象,其中包含与原始对象相同的值。
在address
字段中,我们声明了一个指向Address
对象的引用。当我们更新Address
对象中的city
字段时,我们发现这个改变同时反映在了p1
和p2
中。
Tom, 20, Beijing
Tom, 20, Beijing
Shanghai
Shanghai
因为在浅克隆中,克隆的是引用地址,所以address
字段实际上是Person
对象和p2
对象所共享的。所以在改变addr
对象中的city
字段时,这个改变也被反映在p1
和p2
中。
深克隆是指克隆整个Java对象,包括其字段中的对象。这种克隆方式将创建一个对象副本,其中包含与原始对象不同的内存地址。
和浅克隆不同的是,深克隆中对于原始对象中的对象字段,会创建一个新对象进行复制,而不是共享引用。
下面是一个例子:
import java.io.*;
class Person implements Cloneable, Serializable {
public String name;
public int age;
public Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
try {
return ois.readObject();
} catch (ClassNotFoundException e) {
return null;
}
}
}
class Address implements Serializable {
public String city;
public Address(String city) {
this.city = city;
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Address addr = new Address("Beijing");
Person p1 = new Person("Tom", 20, addr);
Person p2 = (Person) p1.clone();
System.out.println(p1.name + ", " + p1.age + ", " + p1.address.city);
System.out.println(p2.name + ", " + p2.age + ", " + p2.address.city);
addr.city = "Shanghai";
System.out.println(p1.address.city); //输出"Shanghai"
System.out.println(p2.address.city); //输出"Beijing"
}
}
在这个例子中,我们使用了Java序列化机制来实现深克隆。当我们调用clone()
方法时,它会将对象序列化到一个字节数组中。然后再将其反序列化到一个新的Person
对象中,这样就得到了一个完整的深度克隆对象。
注意到Address
类必须也要实现序列化接口才能深度克隆Person
对象。
在这个例子中,我们可以看到浅克隆和深克隆的不同点。由于深克隆中所有的对象都会被复制,所以当我们改变addr
对象中的city
字段时,只有原始对象中的address
字段被修改了,而复制出来的p2
对象中的address
字段并没有被修改。
Tom, 20, Beijing
Tom, 20, Beijing
Shanghai
Beijing
总结:Java对象克隆技术是一种很有用的特性,可以通过这种技术方便的创建一个新的对象。对于一些特定业务场景,我们可以通过实现Cloneable
接口或者序列化接口来实现浅克隆或者深克隆。