簡(jiǎn)述Java語(yǔ)言的對(duì)象克隆特性
在Java中傳值及引伸深度克隆的思考中,我們講過(guò)引申到克隆技術(shù)Java中的所有對(duì)象都是Object類的子類。我們知道,Java是純面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言。Java里,所有的類的***父類都是java.lang.Object類,也就是說(shuō),如果一個(gè)類沒(méi)有顯示 申明繼承關(guān)系,它的父類默認(rèn)就是java.lang.Object。
有一個(gè)很簡(jiǎn)單的方法可以證明這一點(diǎn),我們寫一個(gè)Test類,如下:
- public class Test {
 - public void someMethod() {
 - super.clone();
 - }
 - }
 
里面調(diào)用了super.clone(),編譯時(shí)并不報(bào)錯(cuò)。其實(shí)clone()方法為java.lang.Object類提供的一個(gè) protected型方法。
對(duì)象克隆
本文通過(guò)介紹java.lang.Object#clone()方法來(lái)說(shuō)明Java語(yǔ)言的對(duì)象克隆特性。
java.lang.Object#clone()方法由java.lang.Object加以實(shí)現(xiàn),主要對(duì)對(duì)象本身加以克隆。
首先我們看看下面的例子:
- public class TestClone {
 - public static void main(String[] args) {
 - MyClone myClone1 = new MyClone("clone1");
 - MyClone myClone2 = (MyClone)myClone1.clone();
 - if (myClone2 != null) {
 - System.out.println(myClone2.getName());
 - System.out.println("myClone2 equals myClone1: " + myClone2.equals(myClone1));
 - } else {
 - System.out.println("Clone Not Supported");
 - }
 - }
 - }
 - class MyClone {
 - private String name;
 - public MyClone(String name) {
 - this.name = name;
 - }
 - public String getName() {
 - return name;
 - }
 - public void setName(String name) {
 - this.name = name;
 - }
 - public Object clone() {
 - try {
 - return super.clone();
 - } catch (CloneNotSupportedException e) {
 - return null;
 - }
 - }
 
編譯執(zhí)行TestClone,打印出:
- C:\clone>javac *.java
 - C:\clone>java TestClone
 - Clone Not Supported
 - C:\clone>
 
說(shuō)明MyClone#clone()方法調(diào)用super.clone()時(shí)拋出了CloneNotSupportedException異常,不支持克隆。
為什么父類java.lang.Object里提供了clone()方法,卻不能調(diào)用呢?
原來(lái),Java語(yǔ)言雖然提供了這個(gè)方法,但考慮到安全問(wèn)題, 一方面將clone()訪問(wèn)級(jí)別設(shè)置為protected型,以限制外部類訪問(wèn);
另一方面,強(qiáng)制需要提供clone功能的子類實(shí)現(xiàn)java.lang.Cloneable接口,在運(yùn)行期,JVM會(huì)檢查調(diào)用clone()方法的 類,如果該類未實(shí)現(xiàn)java.lang.Cloneable接口,則拋出CloneNotSupportedException異常。
java.lang.Cloneable接口是一個(gè)空的接口,沒(méi)有申明任何屬性與方法。該接口只是告訴JVM,該接口的實(shí)現(xiàn)類需要開放“克隆”功能。
我們?cè)賹yClone類稍作改變,讓其實(shí)現(xiàn)Cloneable接口:
- class MyClone implements Cloneable {
 - ...//其余不做改變
 - }
 - 編譯執(zhí)行TestClone,打印出:
 - C:\clone>javac *.java
 - C:\clone>java TestClone
 - clone1
 - myClone2 equals myClone1: false
 - C:\clone>
 
根據(jù)結(jié)果,我們可以發(fā)現(xiàn):
1,myClone1.clone()克隆了跟myClone1具有相同屬性值的對(duì)象
2,但克隆出的對(duì)象myClone2跟myClone1不是同一個(gè)對(duì)象(具有不同的內(nèi)存空間)
小結(jié)
如果要讓一個(gè)類A提供克隆功能,該類必須實(shí)現(xiàn)java.lang.Cloneable接口,并重載 java.lang.Object#clone()方法。
- public class A extends Cloneable {
 - public Object clone() {
 - try {
 - return super.clone();
 - } catch (CloneNotSupportedException e) {
 - //throw (new InternalError(e.getMessage()));
 - return null;
 - }
 - }
 - }
 
對(duì)象的深層次克隆
上例說(shuō)明了怎么樣克隆一個(gè)具有簡(jiǎn)單屬性(String,int,boolean等)的對(duì)象。
但如果一個(gè)對(duì)象的屬性類型是List,Map,或者用戶自定義的其他類時(shí),克隆行為是通過(guò)怎樣的方式進(jìn)行的?
很多時(shí)候,我們希望即使修改了克隆后的對(duì)象的屬性值,也不會(huì)影響到原對(duì)象,這種克隆我們稱之為對(duì)象的深層次克隆。怎么樣實(shí)現(xiàn)對(duì)象的深層次克隆呢?
驗(yàn)證對(duì)象的克隆方式
為了驗(yàn)證對(duì)象的克隆方式,我們對(duì)上面的例子加以改進(jìn),如下(為了節(jié)省篇幅,我們省略了setter與getter方法):
- public class TestClone {
 - public static void main(String[] args) {
 - //為克隆對(duì)象設(shè)置值
 - MyClone myClone1 = new MyClone("clone1");
 - myClone1.setBoolValue(true);
 - myClone1.setIntValue(100);
 - //設(shè)置List值
 - List <Element>listValue = new ArrayList<Element>();
 - listValue.add(new Element("ListElement1"));
 - listValue.add(new Element("ListElement2"));
 - listValue.add(new Element("ListElement3"));
 - myClone1.setListValue(listValue);
 - //設(shè)置Element值
 - Element element1 = new Element("element1");
 - myClone1.setElement(element1);
 - //克隆
 - MyClone myClone2 = (MyClone)myClone1.clone();
 - if (myClone2 != null) {
 - //簡(jiǎn)單屬性
 - System.out.println("myClone2.name=" + myClone2.getName()
 - + " myClone2.boolValue=" + myClone2.isBoolValue()
 - + " myClone2.intValue=" + myClone2.getIntValue() );
 - //復(fù)合屬性(List<Element>與Element)
 - List clonedList = myClone2.getListValue();
 - Element element2 = 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());
 - //下面我們測(cè)試一下myClone2.element是否等于myClone1.element
 - //以及myClone2.listValue是否等于myClone1.listValue
 - //為此,我們修改myClone2.element與myClone2.listValue,如果myClone1的相應(yīng)值也跟著被修改了,則它們引用 的是同一個(gè)內(nèi)存空間的變量,我們認(rèn)為它們相等
 - 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("Clone Not Supported");
 - }
 - }
 - }
 - class MyClone implements Cloneable {
 - private int intValue;
 - private boolean boolValue;
 - private String name;
 - private List <Element>listValue;
 - private Element element;
 - public MyClone(String name) {
 - this.name = name;
 - }
 - ...//setter與getter方法(略)
 - }
 - class Element implements Cloneable {
 - private String name;
 - public Element (String name) {
 - this.name = name;
 - }
 - ...//setter與getter方法(略)
 - }
 
編譯執(zhí)行TestClone,打印出:
- C:\clone>javac *.java
 - C:\clone>java TestClone
 - myClone2.name=clone1 myClone2.boolValue=true myClone2.intValue=100
 - myClone2.listValue.size():3
 - myClone2.element.equals(myClone1.element):true
 - myClone2.element.name:element1
 - myClone1.listValue.size():4
 - myClone1.element.name:Element2
 - myClone2 equals myClone1: false
 - C:\clone>
 
我們發(fā)現(xiàn),對(duì)于對(duì)象里的List,Element等復(fù)合屬性,super.clone()只是簡(jiǎn)單地賦值,沒(méi)有采取克隆手段。也就是說(shuō),修改被克 隆后的對(duì)象值,會(huì)影響到原對(duì)象。
怎么進(jìn)行深層次的克隆呢?
答案是,我們只能手動(dòng)在重載的clone()方法里,對(duì)屬性也分別采用克隆操作。當(dāng)然條件是,屬性類也得支持克隆操作
- class MyClone implements Cloneable {
 - ...
 - public Object clone() {
 - try {
 - MyClone myClone = (MyClone)super.clone();
 - //分別對(duì)屬性加以克隆操作
 - myClone.element = this.element.clone();
 - myClone.listValue = new ArrayList();
 - for (Element ele:this.listValue) {
 - myClone.listValue.add(ele.clone());
 - }
 - return myClone;
 - } catch (CloneNotSupportedException e) {
 - return null;
 - }
 - }
 - ...
 - }
 - //讓Element類也支持克隆操作
 - class Element implements Cloneable {
 - ...
 - public Element clone() {
 - try {
 - return (Element)super.clone();
 - } catch (CloneNotSupportedException e) {
 - return null;
 - }
 - }
 - }
 
深層次的克隆操作往往存在效率問(wèn)題,尤其是需要讓List,Map等集合類也支持深層次的克隆操作時(shí)。
總結(jié)
本文結(jié)合范例,比較深入地介紹了Java語(yǔ)言的克隆屬性,以及克隆的實(shí)現(xiàn)方法等。同時(shí)分析了深層次克隆的概念,實(shí)現(xiàn),以及存在的問(wèn)題等。 但是有沒(méi)有更好的方法呢?當(dāng)然,是有的,串行化來(lái)實(shí)現(xiàn)。
【編輯推薦】















 
 
 

 
 
 
 