clone
传函数参数时的,传入基本类型 和 对象的效果是不一样的。 String对象的话 好像还有点特别,有待进一步实验验证 或者 查资料
影子clone 深度clone。
先看一下 clone的使用:
1)类要实现Clonable接口,虽然这个接口中什么都没有
2)在类中重写clone方法,因为Object中的clone接口是protect修饰的,所以在自己创建的类中需要重写这个方法,将其声明为public修饰,并且需要调用 Object的clone方法,这是一个 native本地方法。
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
A a=new A();
a.setA(19);
System.out.println(a);
A clone = (A)a.clone();
System.out.println(clone.getA());
System.out.println(clone);
}
}
class A implements Cloneable{
int a=2; public int getA() { return a; }
public void setA(int a) { this.a = a; }
public A(){ System.out.println("A执行了构造函数"); }
@Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }

可以看到,通过clone来创建对象是不会调用构造函数的,并且clone出来的对象和原来的对象不是同一个对象。那么对象是怎么被创建出来的呢?
影子克隆
clone是将引用地址传递过来了,还是新创建了一个新的实例,然后传递过来了?
通过上面我们知道,通过clone方法创建A的实例时,是创建了一个新的A的实例,因为clone得到的实例与原来的实例的内存地址是不一样的,但是实例中的属性是重新创建的还是指向原来的地址呢? 通过下面这个例子,可以看出,对于基本类型是创建了一份复制,也就是clone后的实例修改实例中的基本类型的属性不会影响原来的实例中的数。但是,对于非基本类型呢,通过下面代码,我们发现是传来的与原对象实例相同的引用地址,那么修改clone后的对象的非基本类型的属性的值,会影响原来的对象实例。(注意:这里指的修改非基本类型的值,是修改非基本类型中的属性数据,而不是修改这个非基本类型的引用地址,因为只有两个引用指向了同一个地址,才会出现,一个对地址所指向的实例中属性数值的修改,另一个同样引用的,也会发生改变,因为地址指向是一样的)
public class CloneTest { public static void main(String[] args) throws CloneNotSupportedException { A a=new A(); a.setB(new B(22)); a.setA(19); A clone = (A)a.clone(); a.setA(00); a.getB().setB(11); System.out.println(a); System.out.println(clone); } } class A implements Cloneable{ int a=2; B b; public B getB() { return b; } public void setB(B b) { this.b = b; } public int getA() { return a; } public void setA(int a) { this.a = a; } public A(){ System.out.println("A执行了构造函数"); } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "A{" + "a=" + a + ", b=" + b + '}'; } } class B { int b=7; public B(int b) { System.out.println("调用了B的构造函数"); this.b = b; } public int getB() { return b; } public void setB(int b) { this.b = b; } @Override public String toString() { return "B{" + "b=" + b + '}'; } }

深度克隆
那么,怎么让非基本类型的属性也不指向与原来对象一样的引用地址,就需要将这个对象也 实现 Cloneable 接口,重写它的 clone方法,这样就行了吗?
也就是在B类中也写一个这样的重载方法,并且B也实现Cloneable接口
@Override public Object clone() throws CloneNotSupportedException { return super.clone(); }
【不经过自己深度思考获取的知识,不是自己的知识!】
想一下,这样与上面那个例子,有什么区别? 好像只有B类多实现了一个接口,并且多写了一个clone重写方法。但是A在执行clone方法的时候,仍然会按照原来的方式进行,也就是并没有发生变化。clone后的属性b与原来的a实例指向的b,仍然是同一个。如果想让它不是同一个,就需要改变它的指向,也就是在A的clone方法中改变属性b的引用。怎么做? 调用B的clone方法。(当然你也可以选择重新new 一个B, 也可以选择通过反射来创建一个新的B,到那时它们的效率不要低于clone方式)
修改A的clone方法:
@Override public Object clone() throws CloneNotSupportedException { A clone = (A)super.clone(); // 得到 A的clone实例 clone.b=(B)b.clone(); // 修改clone后的 A实例的 b属性 (将B也clone了一下) 这里要注意一点,要调用B实例的clone方法,要保证A中的b属性不是null return clone; }

这时,可以看到 clone后的A的实例与原来的A的实例指向的属性b已经不是同一个了。
!!! 如果在A中有一个String类型的属性呢
class A implements Cloneable{
int a=2;
B b;
String c="sss";
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
..... 省略掉其它代码
}
public class CloneTest { public static void main(String[] args) throws CloneNotSupportedException { A a=new A(); a.setB(new B(22)); a.setA(19); A clone = (A)a.clone(); a.setA(00); a.getB().setB(11); a.setC("cccc"); System.out.println(a); System.out.println(clone); } }

可以看到,colone前后的a实例的c属性指向的地址是不一样的,可以我们没有写他的clone方法啊,当然我们也写不了,因为String类是一个final修饰的类,那么这是怎么回事呢??
这很正常,因为调用 setC(String c)方法,就是传入的一个新的对象c , 并把c的地址赋给了实例a中的 c属性。不止是String类型的,任何类型的都是这样。
【只有真正理解,才能真正理解】
参考文档:
这篇写的挺好的: https://www.cnblogs.com/shoshana-kong/p/10820420.html
浙公网安备 33010602011771号