简述Java语言的对象克隆特性

在Java中传值及引伸深度克隆的思考中,我们讲过引申到克隆技术Java中的所有对象都是Object类的子类。我们知道,Java是纯面向对象的程序设计语言。Java里,所有的类的***父类都是java.lang.Object类,也就是说,如果一个类没有显示 申明继承关系,它的父类默认就是java.lang.Object。

有一个很简单的方法可以证明这一点,我们写一个Test类,如下:

 
 
 
  1. public class Test { 
  2.     public void someMethod() { 
  3.         super.clone(); 
  4.     } 

里面调用了super.clone(),编译时并不报错。其实clone()方法为java.lang.Object类提供的一个 protected型方法。

对象克隆

本文通过介绍java.lang.Object#clone()方法来说明Java语言的对象克隆特性。

java.lang.Object#clone()方法由java.lang.Object加以实现,主要对对象本身加以克隆。

首先我们看看下面的例子:

 
 
 
  1. public class TestClone { 
  2.     public static void main(String[] args) { 
  3.         MyClone myClone1 = new MyClone("clone1"); 
  4.         
  5.         MyClone myClone2 = (MyClone)myClone1.clone(); 
  6.         
  7.         if (myClone2 != null) { 
  8.             System.out.println(myClone2.getName()); 
  9.             System.out.println("myClone2 equals myClone1: " + myClone2.equals(myClone1)); 
  10.         } else { 
  11.             System.out.println("Clone Not Supported"); 
  12.         } 
  13.     } 
  14. class MyClone { 
  15.     private String name; 
  16.     public MyClone(String name) { 
  17.         this.name = name; 
  18.     } 
  19.     
  20.     public String getName() { 
  21.         return name; 
  22.     } 
  23.     public void setName(String name) { 
  24.         this.name = name; 
  25.     } 
  26.     
  27.     public Object clone() { 
  28.         try { 
  29.             return super.clone(); 
  30.         } catch (CloneNotSupportedException e) { 
  31.             return null; 
  32.         } 
  33.     } 

编译执行TestClone,打印出:

 
 
 
  1. C:\clone>javac *.java 
  2. C:\clone>java TestClone 
  3. Clone Not Supported 
  4. C:\clone> 

说明MyClone#clone()方法调用super.clone()时抛出了CloneNotSupportedException异常,不支持克隆。

为什么父类java.lang.Object里提供了clone()方法,却不能调用呢?

原来,Java语言虽然提供了这个方法,但考虑到安全问题, 一方面将clone()访问级别设置为protected型,以限制外部类访问;

另一方面,强制需要提供clone功能的子类实现java.lang.Cloneable接口,在运行期,JVM会检查调用clone()方法的 类,如果该类未实现java.lang.Cloneable接口,则抛出CloneNotSupportedException异常。

java.lang.Cloneable接口是一个空的接口,没有申明任何属性与方法。该接口只是告诉JVM,该接口的实现类需要开放“克隆”功能。

我们再将MyClone类稍作改变,让其实现Cloneable接口:

 
 
 
  1. class MyClone implements Cloneable { 
  2.     ...//其余不做改变 
  3. 编译执行TestClone,打印出: 
  4. C:\clone>javac *.java 
  5. C:\clone>java TestClone 
  6. clone1 
  7. myClone2 equals myClone1: false 
  8. C:\clone> 

根据结果,我们可以发现:

1,myClone1.clone()克隆了跟myClone1具有相同属性值的对象

2,但克隆出的对象myClone2跟myClone1不是同一个对象(具有不同的内存空间)

小结

如果要让一个类A提供克隆功能,该类必须实现java.lang.Cloneable接口,并重载 java.lang.Object#clone()方法。

 
 
 
  1. public class A extends Cloneable { 
  2.     public Object clone() { 
  3.         try { 
  4.             return super.clone(); 
  5.         } catch (CloneNotSupportedException e) { 
  6.             //throw (new InternalError(e.getMessage())); 
  7.             return null; 
  8.         } 
  9.     } 

对象的深层次克隆

上例说明了怎么样克隆一个具有简单属性(String,int,boolean等)的对象。

但如果一个对象的属性类型是List,Map,或者用户自定义的其他类时,克隆行为是通过怎样的方式进行的?

很多时候,我们希望即使修改了克隆后的对象的属性值,也不会影响到原对象,这种克隆我们称之为对象的深层次克隆。怎么样实现对象的深层次克隆呢?

验证对象的克隆方式

为了验证对象的克隆方式,我们对上面的例子加以改进,如下(为了节省篇幅,我们省略了setter与getter方法):

 
 
 
  1. public class TestClone { 
  2.     public static void main(String[] args) { 
  3.         //为克隆对象设置值 
  4.          MyClone myClone1 = new MyClone("clone1"); 
  5.          myClone1.setBoolValue(true); 
  6.          myClone1.setIntValue(100); 
  7.         
  8.         //设置List值 
  9.          List listValue = new ArrayList(); 
  10.          listValue.add(new Element("ListElement1")); 
  11.          listValue.add(new Element("ListElement2")); 
  12.          listValue.add(new Element("ListElement3")); 
  13.          myClone1.setListValue(listValue); 
  14.         
  15.         //设置Element值 
  16.          Element element1 = new Element("element1"); 
  17.          myClone1.setElement(element1); 
  18.         
  19.         
  20.         //克隆 
  21.          MyClone myClone2 = (MyClone)myClone1.clone(); 
  22.         
  23.         if (myClone2 != null) { 
  24.             
  25.             //简单属性 
  26.              System.out.println("myClone2.name=" + myClone2.getName() 
  27.                      + " myClone2.boolValue=" + myClone2.isBoolValue() 
  28.                      + " myClone2.intValue=" + myClone2.getIntValue() ); 
  29.             
  30.             //复合属性(List与Element) 
  31.              List clonedList = myClone2.getListValue(); 
  32.              Element element2 = myClone2.getElement(); 
  33.             
  34.              System.out.println("myClone2.listValue.size():" + clonedList.size()); 
  35.              System.out.println("myClone2.element.equals(myClone1.element):" + element2.equals(element1)); 
  36.              System.out.println("myClone2.element.name:" + element2.getName()); 
  37.             
  38.             //下面我们测试一下myClone2.element是否等于myClone1.element 
  39.             //以及myClone2.listValue是否等于myClone1.listValue 
  40.             //为此,我们修改myClone2.element与myClone2.listValue,如果myClone1的相应值也跟着被修改了,则它们引用 的是同一个内存空间的变量,我们认为它们相等 
  41.             
  42.              clonedList.add("ListElement4"); 
  43.             
  44.              System.out.println("myClone1.listValue.size():" + listValue.size()); 
  45.             
  46.              element2.setName("Element2"); 
  47.              System.out.println("myClone1.element.name:" + element1.getName()); 
  48.             
  49.          } else { 
  50.              System.out.println("Clone Not Supported"); 
  51.          }        
  52.         
  53.      } 
  54. class MyClone implements Cloneable { 
  55.     private int intValue; 
  56.     private boolean boolValue; 
  57.     private String name; 
  58.     private List listValue; 
  59.     private Element element; 
  60.     public MyClone(String name) { 
  61.          this.name = name; 
  62.      } 
  63.      ...//setter与getter方法(略) 
  64. class Element implements Cloneable   { 
  65.     private String name; 
  66.     
  67.     public Element (String name) { 
  68.          this.name = name; 
  69.      } 
  70.      ...//setter与getter方法(略) 

编译执行TestClone,打印出:

 
 
 
  1. C:\clone>javac *.java 
  2. C:\clone>java TestClone 
  3. myClone2.name=clone1 myClone2.boolValue=true myClone2.intValue=100 
  4. myClone2.listValue.size():3 
  5. myClone2.element.equals(myClone1.element):true 
  6. myClone2.element.name:element1 
  7. myClone1.listValue.size():4 
  8. myClone1.element.name:Element2 
  9. myClone2 equals myClone1: false 
  10. C:\clone> 

我们发现,对于对象里的List,Element等复合属性,super.clone()只是简单地赋值,没有采取克隆手段。也就是说,修改被克 隆后的对象值,会影响到原对象。

怎么进行深层次的克隆呢?

答案是,我们只能手动在重载的clone()方法里,对属性也分别采用克隆操作。当然条件是,属性类也得支持克隆操作

 
 
 
  1. class MyClone implements Cloneable { 
  2.      ... 
  3.     public Object clone() { 
  4.         try { 
  5.              MyClone myClone = (MyClone)super.clone(); 
  6.             //分别对属性加以克隆操作 
  7.              myClone.element = this.element.clone(); 
  8.             
  9.              myClone.listValue = new ArrayList(); 
  10.             for (Element ele:this.listValue) { 
  11.                  myClone.listValue.add(ele.clone()); 
  12.              } 
  13.                         
  14.             return myClone; 
  15.          } catch (CloneNotSupportedException e) { 
  16.             return null; 
  17.          } 
  18.      } 
  19.      ... 
  20. //让Element类也支持克隆操作 
  21. class Element implements Cloneable   { 
  22.      ... 
  23.     public Element clone() { 
  24.         try { 
  25.             return (Element)super.clone(); 
  26.          } catch (CloneNotSupportedException e) { 
  27.             return null; 
  28.          } 
  29.      } 

深层次的克隆操作往往存在效率问题,尤其是需要让List,Map等集合类也支持深层次的克隆操作时。

总结

本文结合范例,比较深入地介绍了Java语言的克隆属性,以及克隆的实现方法等。同时分析了深层次克隆的概念,实现,以及存在的问题等。 但是有没有更好的方法呢?当然,是有的,串行化来实现。

网站名称:简述Java语言的对象克隆特性
本文网址:http://www.csdahua.cn/qtweb/news11/338561.html

网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网