欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

详解Java中的clone方法:原型模式(1)(2)

来源: javaer 分享于  点击 47154 次 点评:189

深拷贝 or 浅拷贝

上面的示例代码中,Person中有两个成员变量,分别是name和age, name是String类型, age是int类型。代码非常简单,如下所示:

  1. public class Person implements Cloneable{ 
  2. private int age ; 
  3. private String name; 
  4. public Person(int age, String name) { 
  5.   this.age = age; 
  6.   this.name = name; 
  7. public Person() {} 
  8.  
  9. public int getAge() { 
  10.   return age; 
  11. public String getName() { 
  12.   return name; 
  13. @Override 
  14. protected Object clone() throws CloneNotSupportedException { 
  15.   return (Person)super.clone(); 

由于age是基本数据类型, 那么对它的拷贝没有什么疑议,直接将一个4字节的整数值拷贝过来就行。但是name是String类型的, 它只是一个引用, 指向一个真正的String对象,那么对它的拷贝有两种方式: 直接将源对象中的name的引用值拷贝给新对象的name字段, 或者是根据原Person对象中的name指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的Person对象的 name字段。这两种拷贝方式分别叫做浅拷贝和深拷贝。深拷贝和浅拷贝的原理如下图所示:

详解Java中的clone方法 — 原型模式

下面通过代码进行验证。如果两个Person对象的name的地址值相同, 说明两个对象的name都指向同一个String对象, 也就是浅拷贝, 而如果两个对象的name的地址值不同, 那么就说明指向不同的String对象, 也就是在拷贝Person对象的时候, 同时拷贝了name引用的String对象, 也就是深拷贝。验证代码如下:

  1. Person p = new Person(23"zhang");  
  2. Person p1 = (Person) p.clone();  
  3.  
  4. String result = p.getName() == p1.getName()   
  5.         ? "clone是浅拷贝的" : "clone是深拷贝的";  
  6.  
  7. System.out.println(result); 

打印结果为:

clone是浅拷贝的

所以,clone方法执行的是浅拷贝, 在编写程序时要注意这个细节。

覆盖Object中的clone方法, 实现深拷贝

现在为了要在clone对象时进行深拷贝, 那么就要Clonable接口,覆盖并实现clone方法,除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来。如果只是用Object中默认的clone方法,是浅拷贝的,再次以下面的代码验证:

  1. static class Body implements Cloneable{  
  2.     public Head head;  
  3.  
  4.     public Body() {}  
  5.  
  6.     public Body(Head head) {this.head = head;}  
  7.  
  8.     @Override  
  9.     protected Object clone() throws CloneNotSupportedException {  
  10.         return super.clone();  
  11.     }  
  12.  
  13. }  
  14. static class Head /*implements Cloneable*/{  
  15.     public  Face face;  
  16.  
  17.     public Head() {}  
  18.     public Head(Face face){this.face = face;}  
  19.  
  20. }   
  21. public static void main(String[] args) throws CloneNotSupportedException {  
  22.  
  23.     Body body = new Body(new Head());  
  24.  
  25.     Body body1 = (Body) body.clone();  
  26.  
  27.     System.out.println("body == body1 : " + (body == body1) );  
  28.  
  29.     System.out.println("body.head == body1.head : " +  (body.head == body1.head));  
  30.  

在以上代码中, 有两个主要的类, 分别为Body和Face, 在Body类中, 组合了一个Face对象。当对Body对象进行clone时, 它组合的Face对象只进行浅拷贝。打印结果可以验证该结论:

  1. body == body1 : false 
  2. body.head == body1.head : true 

如果要使Body对象在clone时进行深拷贝, 那么就要在Body的clone方法中,将源对象引用的Head对象也clone一份。

  1. static class Body implements Cloneable{  
  2.     public Head head;  
  3.     public Body() {}  
  4.     public Body(Head head) {this.head = head;}  
  5.  
  6.     @Override  
  7.     protected Object clone() throws CloneNotSupportedException {  
  8.         Body newBody =  (Body) super.clone();  
  9.         newBody.head = (Head) head.clone();  
  10.         return newBody;  
  11.     }  
  12.  
  13. }  
  14. static class Head implements Cloneable{  
  15.     public  Face face;  
  16.     public Head() {}  
  17.     public Head(Face face){this.face = face;}  
  18.     @Override  
  19.     protected Object clone() throws CloneNotSupportedException {  
  20.         return super.clone();  
  21.     }  
  22. }   
  23. public static void main(String[] args) throws CloneNotSupportedException {  
  24.  
  25.     Body body = new Body(new Head());  
  26.  
  27.     Body body1 = (Body) body.clone();  
  28.  
  29.     System.out.println("body == body1 : " + (body == body1) );  
  30.  
  31.     System.out.println("body.head == body1.head : " +  (body.head == body1.head));  
  32.  

打印结果为:

  1. body == body1 : false 
  2. body.head == body1.head : false 

由此可见, body和body1内的head引用指向了不同的Head对象, 也就是说在clone Body对象的同时, 也拷贝了它所引用的Head对象, 进行了深拷贝。




相关栏目:

用户点评