赞
踩
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone();
System.out.println(p);
System.out.println(p1)
这段代码的打印结果为:
testclone.Person@2f9ee1ac
testclone.Person@2f9ee1ac
可以看出两者打印的地址是相同的,既然地址相同那就代表两者是同一个对象,而p和p1都只是引用罢了。代码执行后,内存中的情况
而下面这段代码才是真的克隆了一个对象
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone();
System.out.println(p);
System.out.println(p1)
打印的结果为:
testclone.Person@2f9ee1ac
testclone.Person@67f1fba0
从结果来看两者不是同一个地址,那自然就不是同一个对象,而内存情况如下
二、深拷贝和浅拷贝
在上面的代码中Person中有两个成员变量,分别是name和age, name是String类型, age是int类型。代码非常简单,如下所示:
public class Person implements Cloneable{ private int age ; private String name; public Person(int age, String name) { this.age = age; this.name = name; } public Person() {} public int getAge() { return age; } public String getName() { return name; } @Override protected Object clone() throws CloneNotSupportedException { return (Person)super.clone(); } }
Person类的两个成员变量中,age是基本数据类型,关于基本数据类型的拷贝在上面就已经说过了,但是name是引用数据类型,它只是一个引用, 指向一个真正的String对象,那么对它的拷贝有两种方式: 直接将源对象中的name的引用值拷贝给新对象的name字段, 或者是根据原Person对象中的name指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的Person对象的name字段。这两种拷贝方式分别叫做浅拷贝和深拷贝。深拷贝和浅拷贝的原理如下图所示:
下面我们通过代码来验证:如果两个Person对象的name的地址值相同, 说明两个对象的name都指向同一个String对象, 也就是浅拷贝, 而如果两个对象的name的地址值不同, 那么就说明指向不同的String对象, 也就是在拷贝Person对象的时候, 同时拷贝了name引用的String对象, 也就是深拷贝。验证代码如下:
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone();
String result = p.getName() == p1.getName()
? "clone是浅拷贝的" : "clone是深拷贝的";
System.out.println(result);
打印的结果为:
clone是浅拷贝
也就是clone方法本身是一个浅拷贝的方法,而上一个代码我们又得出clone是深拷贝,那么是我前面写错了吗?那当然不是,clone方法本身是浅拷贝的,但是我们可以通过重写该方法来实现深拷贝 ,所以要实现深拷贝我们就需要实现Cloneable接口,覆盖并实现clone方法,除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来。如果只是用Object中默认的clone方法,是浅拷贝的,再次以下面的代码验证:
static class Body implements Cloneable{ public Head head; public Body() {} public Body(Head head) {this.head = head;} @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } static class Head /*implements Cloneable*/{ public Face face; public Head() {} public Head(Face face){this.face = face;} } public static void main(String[] args) throws CloneNotSupportedException { Body body = new Body(new Head()); Body body1 = (Body) body.clone(); System.out.println("body == body1 : " + (body == body1) ); System.out.println("body.head == body1.head : " + (body.head == body1.head)); }
在上述代码中有两个主要的类,Body和Face,Body类中又组合了Face对象,在对Body进行clone时,它所组合的Face对象时浅拷贝,其打印结果可以很好的证明这一点:
body == body1 : false
body.head == body1.head : true
如果要使Body对象在clone时进行深拷贝, 那么就要在Body的clone方法中,将源对象引用的Head对象也clone一份。
static class Body implements Cloneable{ public Head head; public Body() {} public Body(Head head) {this.head = head;} @Override protected Object clone() throws CloneNotSupportedException { Body newBody = (Body) super.clone(); newBody.head = (Head) head.clone(); return newBody; } } static class Head implements Cloneable{ public Face face; public Head() {} public Head(Face face){this.face = face;} @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } public static void main(String[] args) throws CloneNotSupportedException { Body body = new Body(new Head()); Body body1 = (Body) body.clone(); System.out.println("body == body1 : " + (body == body1) ); System.out.println("body.head == body1.head : " + (body.head == body1.head)); }
打印结果为:
body == body1 : false
body.head == body1.head : false
由此可见, body和body1内的head引用指向了不同的Head对象, 也就是说在clone Body对象的同时, 也拷贝了它所引用的Head对象, 进行了深拷贝。
从以上的内容中我们可以知道,一个对象要实现深拷贝,就要实现Cloneable接口,重写clone()方法,并在clone()方法中将对象引用的其他对象也要clone一份,这就要求被引用的对象也要实现Cloneable接口,并实现clone()方法。
而按照这个结论,Body类中组合了Head类,而Head类中又组合了Face类,在对Body对象进行深拷贝时,会拷贝其中的Head类,而这时默认执行的时浅拷贝,也就是说其中的Face对象并不会被拷贝,代码如下:
static class Body implements Cloneable{ public Head head; public Body() {} public Body(Head head) {this.head = head;} @Override protected Object clone() throws CloneNotSupportedException { Body newBody = (Body) super.clone(); newBody.head = (Head) head.clone(); return newBody; } } static class Head implements Cloneable{ public Face face; public Head() {} public Head(Face face){this.face = face;} @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } static class Face{} public static void main(String[] args) throws CloneNotSupportedException { Body body = new Body(new Head(new Face())); Body body1 = (Body) body.clone(); System.out.println("body == body1 : " + (body == body1) ); System.out.println("body.head == body1.head : " + (body.head == body1.head)); System.out.println("body.head.face == body1.head.face : " + (body.head.face == body1.head.face)); }
打印结果为:
body == body1 : false
body.head == body1.head : false
body.head.face == body1.head.face : true
那我们可以想一想,这对Body对象来说,到底是不是深拷贝,其实是可以算的,因为Body对象中的引用对象是拷贝了的(这段代码中只有Head对象的引用),也就是说两个独立的Body对象中的Head引用指向了两个独立的Head对象,但对于Head对象来说,其Face引用指向了同一个Face对象。所以严格来说这是一种不彻底的深拷贝,而若是想要彻底的深拷贝,就要保证该对象的所有引用对象的类型都要去实现Cloneable接口,实现clone方法。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。