Java克隆详解
首先我们来想一下为什么需要clone 这个方法?
没有clone,其实我们也是可以自己new 对象来实现,不过如果这个对象中有很多属性,并且很多都已经赋值,我们此时想要一个和这个对象一模一样的对象该怎么搞?
手动提取值,然后一个一个set 进去么?太麻烦了,我们如果能实现一个方法自动帮我们来复制其中的属性他不香么?
所以Object 类已经自带了clone 方法,来帮助我们实现对象的克隆。
在这个方法注释我们可以看到他设计这个clone 所需要达成的目标
第一个说明 克隆出来的对象应该有单独的内存地址分配,和原对象不是同一个。
第二个说明,原始和克隆的对象应该属于相同的类类型
第三个说明,原始和克隆的对象应该是相等的
二三两个说明并不是强制的。
下面给出 源码注释的翻译,关键部分我重点画出来了。
按照惯例,返回的对象应该通过调用 super.clone 来获取。如果一个类及其所有超类(Object 除外)都遵循此约定,则 x.clone().getClass() == x.getClass()。
按照约定,此方法返回的对象应独立于此对象(正在克隆的对象)。为了实现这种独立性,可能需要在返回之前修改 super.clone 返回的对象中的一个或多个字段。通常,这意味着复制构成被克隆对象的内部“深层结构”的任何可变对象,并将对这些对象的引用替换为对副本的引用。如果一个类只包含基元字段或对不可变对象的引用,那么通常不需要修改 super.clone 返回的对象中的任何字段。
类对象的方法克隆执行特定的克隆操作。首先,如果此对象的类未实现接口 Cloneable,则会抛CloneNotSupportedException。请注意,所有数组都被视为实现接口 Cloneable,并且数组类型 T[] 的克隆方法的返回类型为 T[],其中 T 是任何引用或基元类型。否则,此方法将创建此对象的类的新实例,并使用该对象的相应字段的内容准确初始化其所有字段,就像通过赋值一样;字段的内容本身不会克隆。因此,此方法执行此对象的“浅拷贝”,而不是“深拷贝”操作。
类 Object 本身不实现接口 Cloneable,因此在类为 Object 的对象上调用克隆方法将导致在运行时引发异常。
这段话的意思我简要说下:
我们的目的是:方法返回的对象应独立于此对象
为了实现这个目的,加入这个对象全是基本数据类型,那没事直接克隆就完事了;但是如果这个对象类型中还定义了其他对象,如果我们没有对其进行处理的话,新克隆出来对象其中的对象引用和原来对象指的是同一个,这就和我们的初衷不符合了。
所以我们重写clone 方法时也要对其内部的对象进行克隆。
public class testClone {
public static void main(String[] args) {
Cpu cpu = new Cpu("酷睿");
Computer computer = new Computer();
computer.setCpu(cpu);
try {
Computer clone = (Computer) computer.clone();
System.out.println("克隆出来的CPU 是"+clone.getCpu().getName());
System.out.println("现在修改原来对象的cpu为锐龙");
cpu.setName("锐龙");
System.out.println("克隆出来的CPU 是"+clone.getCpu().getName());
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
现在我们有一个Computer 类,里面还包含了一个Cpu 类,我们在clone 方法中注释掉了 对内部对象 cpu 的重新克隆
public class Computer implements Cloneable{
private Cpu cpu;
public Cpu getCpu() {
return cpu;
}
public void setCpu(Cpu cpu) {
this.cpu = cpu;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Computer com;
com= (Computer) super.clone();
// com.setCpu(((Cpu) this.cpu));
return com;
}
}
public class Cpu implements Cloneable{
public Cpu(String name) {
this.name = name;
}
private String name;
public Cpu() {
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
执行结果为
我们明明修改的是原对象的CPU,拷贝对象的CPU也变了。
让我们现在取消注释
执行结果为
克隆出来对象的CPU还是酷睿。