如何实现对象克隆?
答:
有两种方式:
1.实现 Cloneable 接口并重写 Object 类中的 clone() 方法;
2.实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
原文链接: https://www.baidu.com/link?url=AUgNSLZgNE8RtkM1QGrOd96y8h-W4L0v47H0HlAl9jNsP1qRf2n1qOjLNrP42syd0uWUnj6vMvn7Pf1-WZYZf_&wd=&eqid=b112af76003c1915000000065ec7754a
实现 Cloneable 接口,重写 clone() 方法。
不实现 Cloneable 接口,会报 CloneNotSupportedException 异常。
package constxiong.interview;
/**
* 测试克隆
* @author ConstXiong
* @date 2019-06-18 11:21:21
*/
public class TestClone {
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person(1, "ConstXiong");//创建对象 Person p1
Person p2 = (Person)p1.clone();//克隆对象 p1
p2.setName("其不答");//修改 p2的name属性,p1的name未变
System.out.println(p1);
System.out.println(p2);
}
}
/**
* 人
* @author ConstXiong
* @date 2019-06-18 11:54:35
*/
class Person implements Cloneable {
private int pid;
private String name;
public Person(int pid, String name) {
this.pid = pid;
this.name = name;
System.out.println("Person constructor call");
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Person [pid:"+pid+", name:"+name+"]";
}
}
打印结果:
Person constructor call
Person [pid:1, name:ConstXiong]
Person [pid:1, name:其不答]
Object 的 clone() 方法是浅拷贝,即如果类中属性有自定义引用类型,只拷贝引用,不拷贝引用指向的对象。
可以使用下面的两种方法,完成 Person 对象的深拷贝。
方法1、对象的属性的Class 也实现 Cloneable 接口,在克隆对象时也手动克隆属性。
@Override
public Object clone() throws CloneNotSupportedException {
DPerson p = (DPerson)super.clone();
p.setFood((DFood)p.getFood().clone());
return p;
}
完整代码:
/**
* 测试克隆
* @author ConstXiong
* @date 2019-06-18 11:21:21
*/
public class TestManalDeepClone {
public static void main(String[] args) throws Exception {
DPerson p1 = new DPerson(1, "ConstXiong", new DFood("米饭"));//创建Person 对象 p1
DPerson p2 = (DPerson)p1.clone();//克隆p1
p2.setName("其不答");//修改p2的name属性
p2.getFood().setName("面条");//修改p2的自定义引用类型 food 属性
System.out.println(p1);//修改p2的自定义引用类型 food 属性被改变,p1的自定义引用类型 food 属性也随之改变,说明p2的food属性,只拷贝了引用,没有拷贝food对象
System.out.println(p2);
}
}
class DPerson implements Cloneable {
private int pid;
private String name;
private DFood food;
public DPerson(int pid, String name, DFood food) {
this.pid = pid;
this.name = name;
this.food = food;
System.out.println("Person constructor call");
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
DPerson p = (DPerson)super.clone();
p.setFood((DFood)p.getFood().clone());
return p;
}
@Override
public String toString() {
return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
}
public DFood getFood() {
return food;
}
public void setFood(DFood food) {
this.food = food;
}
}
class DFood implements Cloneable{
private String name;
public DFood(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
打印结果:
Person constructor call
Person [pid:1, name:ConstXiong, food:米饭]
Person [pid:1, name:其不答, food:面条]
方法2、结合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),完成深拷贝
结合 java.io.Serializable 接口,完成深拷贝
package constxiong.interview;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class TestSeriazableClone {
public static void main(String[] args) {
SPerson p1 = new SPerson(1, "ConstXiong", new SFood("米饭"));//创建 SPerson 对象 p1
SPerson p2 = (SPerson)p1.cloneBySerializable();//克隆 p1
p2.setName("其不答");//修改 p2 的 name 属性
p2.getFood().setName("面条");//修改 p2 的自定义引用类型 food 属性
System.out.println(p1);//修改 p2 的自定义引用类型 food 属性被改变,p1的自定义引用类型 food 属性未随之改变,说明p2的food属性,只拷贝了引用和 food 对象
System.out.println(p2);
}
}
class SPerson implements Cloneable, Serializable {
private static final long serialVersionUID = -7710144514831611031L;
private int pid;
private String name;
private SFood food;
public SPerson(int pid, String name, SFood food) {
this.pid = pid;
this.name = name;
this.food = food;
System.out.println("Person constructor call");
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 通过序列化完成克隆
* @return
*/
public Object cloneBySerializable() {
Object obj = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
obj = ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return obj;
}
@Override
public String toString() {
return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
}
public SFood getFood() {
return food;
}
public void setFood(SFood food) {
this.food = food;
}
}
class SFood implements Serializable {
private static final long serialVersionUID = -3443815804346831432L;
private String name;
public SFood(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
打印结果:
Person constructor call
Person [pid:1, name:ConstXiong, food:米饭]
Person [pid:1, name:其不答, food:面条]