Java克隆对象的特性是什么
Java克隆对象的特性是什么
本篇内容介绍了“Java克隆对象的特性是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
在java面向对象的编程当中,要复制引用类型的对象,就必须克隆对象。通过调用对所有引用类型和对象都是可用的clone方法,来实现克隆。
在Java中传值及引伸深度克隆的思考中,我们讲过引申到克隆技术Java中的所有对象都是Object类的子类。我们知道,Java是纯面向对象的程序设计语言。Java里,所有的类的***父类都是java.lang.Object类,也就是说,如果一个类没有显示 申明继承关系,它的父类默认就是java.lang.Object。
有一个很简单的方法可以证明这一点,我们写一个Test类,如下:
publicclassTest{publicvoidsomeMethod(){super.clone();}}
里面调用了super.clone(),编译时并不报错。其实clone()方法为java.lang.Object类提供的一个 protected型方法。
对象克隆
本文通过介绍java.lang.Object#clone()方法来说明Java语言的对象克隆特性。
java.lang.Object#clone()方法由java.lang.Object加以实现,主要对对象本身加以克隆。
首先我们看看下面的例子:
publicclassTestClone{publicstaticvoidmain(String[]args){MyClonemyClone1=newMyClone("clone1");MyClonemyClone2=(MyClone)myClone1.clone();if(myClone2!=null){System.out.println(myClone2.getName());System.out.println("myClone2equalsmyClone1:"+myClone2.equals(myClone1));}else{System.out.println("CloneNotSupported");}}}classMyClone{privateStringname;publicMyClone(Stringname){this.name=name;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicObjectclone(){try{returnsuper.clone();}catch(CloneNotSupportedExceptione){returnnull;}}
编译执行TestClone,打印出:
C:\clone>javac*.javaC:\clone>javaTestCloneCloneNotSupportedC:\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接口:
classMyCloneimplementsCloneable{...//其余不做改变}编译执行TestClone,打印出:C:\clone>javac*.javaC:\clone>javaTestCloneclone1myClone2equalsmyClone1:falseC:\clone>
根据结果,我们可以发现:
1,myClone1.clone()克隆了跟myClone1具有相同属性值的对象
2,但克隆出的对象myClone2跟myClone1不是同一个对象(具有不同的内存空间)
小结
如果要让一个类A提供克隆功能,该类必须实现java.lang.Cloneable接口,并重载 java.lang.Object#clone()方法。
publicclassAextendsCloneable{publicObjectclone(){try{returnsuper.clone();}catch(CloneNotSupportedExceptione){//throw(newInternalError(e.getMessage()));returnnull;}}}
对象的深层次克隆
上例说明了怎么样克隆一个具有简单属性(String,int,boolean等)的对象。
但如果一个对象的属性类型是List,Map,或者用户自定义的其他类时,克隆行为是通过怎样的方式进行的?
很多时候,我们希望即使修改了克隆后的对象的属性值,也不会影响到原对象,这种克隆我们称之为对象的深层次克隆。怎么样实现对象的深层次克隆呢?
验证对象的克隆方式
为了验证对象的克隆方式,我们对上面的例子加以改进,如下(为了节省篇幅,我们省略了setter与getter方法):
publicclassTestClone{publicstaticvoidmain(String[]args){//为克隆对象设置值MyClonemyClone1=newMyClone("clone1");myClone1.setBoolValue(true);myClone1.setIntValue(100);//设置List值List<Element>listValue=newArrayList<Element>();listValue.add(newElement("ListElement1"));listValue.add(newElement("ListElement2"));listValue.add(newElement("ListElement3"));myClone1.setListValue(listValue);//设置Element值Elementelement1=newElement("element1");myClone1.setElement(element1);//克隆MyClonemyClone2=(MyClone)myClone1.clone();if(myClone2!=null){//简单属性System.out.println("myClone2.name="+myClone2.getName()+"myClone2.boolValue="+myClone2.isBoolValue()+"myClone2.intValue="+myClone2.getIntValue());//复合属性(List<Element>与Element)ListclonedList=myClone2.getListValue();Elementelement2=myClone2.getElement();System.out.println("myClone2.listValue.size():"+clonedList.size());System.out.println("myClone2.element.equals(myClone1.element):"+element2.equals(element1));System.out.println("myClone2.element.name:"+element2.getName());//下面我们测试一下myClone2.element是否等于myClone1.element//以及myClone2.listValue是否等于myClone1.listValue//为此,我们修改myClone2.element与myClone2.listValue,如果myClone1的相应值也跟着被修改了,则它们引用的是同一个内存空间的变量,我们认为它们相等clonedList.add("ListElement4");System.out.println("myClone1.listValue.size():"+listValue.size());element2.setName("Element2");System.out.println("myClone1.element.name:"+element1.getName());}else{System.out.println("CloneNotSupported");}}}classMyCloneimplementsCloneable{privateintintValue;privatebooleanboolValue;privateStringname;privateList<Element>listValue;privateElementelement;publicMyClone(Stringname){this.name=name;}...//setter与getter方法(略)}classElementimplementsCloneable{privateStringname;publicElement(Stringname){this.name=name;}...//setter与getter方法(略)}
编译执行TestClone,打印出:
C:\clone>javac*.javaC:\clone>javaTestClonemyClone2.name=clone1myClone2.boolValue=truemyClone2.intValue=100myClone2.listValue.size():3myClone2.element.equals(myClone1.element):truemyClone2.element.name:element1myClone1.listValue.size():4myClone1.element.name:Element209.myClone2equalsmyClone1:false10.C:\clone>11.
我们发现,对于对象里的List,Element等复合属性,super.clone()只是简单地赋值,没有采取克隆手段。也就是说,修改被克 隆后的对象值,会影响到原对象。
怎么进行深层次的克隆呢?
答案是,我们只能手动在重载的clone()方法里,对属性也分别采用克隆操作。当然条件是,属性类也得支持克隆操作
classMyCloneimplementsCloneable{...publicObjectclone(){try{MyClonemyClone=(MyClone)super.clone();//分别对属性加以克隆操作myClone.element=this.element.clone();myClone.listValue=newArrayList();for(Elementele:this.listValue){myClone.listValue.add(ele.clone());}returnmyClone;}catch(CloneNotSupportedExceptione){returnnull;}}...}//让Element类也支持克隆操作classElementimplementsCloneable{...publicElementclone(){try{return(Element)super.clone();}catch(CloneNotSupportedExceptione){returnnull;}}}
深层次的克隆操作往往存在效率问题,尤其是需要让List,Map等集合类也支持深层次的克隆操作时。
“Java克隆对象的特性是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!