簡述Java語言的對象克隆特性
在Java中傳值及引伸深度克隆的思考中,我們講過引申到克隆技術(shù)Java中的所有對象都是Object類的子類。我們知道,Java是純面向?qū)ο蟮某绦蛟O(shè)計(jì)語言。Java里,所有的類的***父類都是java.lang.Object類,也就是說,如果一個(gè)類沒有顯示 申明繼承關(guān)系,它的父類默認(rèn)就是java.lang.Object。
有一個(gè)很簡單的方法可以證明這一點(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型方法。
對象克隆
本文通過介紹java.lang.Object#clone()方法來說明Java語言的對象克隆特性。
java.lang.Object#clone()方法由java.lang.Object加以實(shí)現(xiàn),主要對對象本身加以克隆。
首先我們看看下面的例子:
- 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>
說明MyClone#clone()方法調(diào)用super.clone()時(shí)拋出了CloneNotSupportedException異常,不支持克隆。
為什么父類java.lang.Object里提供了clone()方法,卻不能調(diào)用呢?
原來,Java語言雖然提供了這個(gè)方法,但考慮到安全問題, 一方面將clone()訪問級(jí)別設(shè)置為protected型,以限制外部類訪問;
另一方面,強(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è)空的接口,沒有申明任何屬性與方法。該接口只是告訴JVM,該接口的實(shí)現(xiàn)類需要開放“克隆”功能。
我們再將MyClone類稍作改變,讓其實(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具有相同屬性值的對象
2,但克隆出的對象myClone2跟myClone1不是同一個(gè)對象(具有不同的內(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;
- }
- }
- }
對象的深層次克隆
上例說明了怎么樣克隆一個(gè)具有簡單屬性(String,int,boolean等)的對象。
但如果一個(gè)對象的屬性類型是List,Map,或者用戶自定義的其他類時(shí),克隆行為是通過怎樣的方式進(jìn)行的?
很多時(shí)候,我們希望即使修改了克隆后的對象的屬性值,也不會(huì)影響到原對象,這種克隆我們稱之為對象的深層次克隆。怎么樣實(shí)現(xiàn)對象的深層次克隆呢?
驗(yàn)證對象的克隆方式
為了驗(yàn)證對象的克隆方式,我們對上面的例子加以改進(jìn),如下(為了節(jié)省篇幅,我們省略了setter與getter方法):
- public class TestClone {
- public static void main(String[] args) {
- //為克隆對象設(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) {
- //簡單屬性
- 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());
- //下面我們測試一下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),對于對象里的List,Element等復(fù)合屬性,super.clone()只是簡單地賦值,沒有采取克隆手段。也就是說,修改被克 隆后的對象值,會(huì)影響到原對象。
怎么進(jìn)行深層次的克隆呢?
答案是,我們只能手動(dòng)在重載的clone()方法里,對屬性也分別采用克隆操作。當(dāng)然條件是,屬性類也得支持克隆操作
- class MyClone implements Cloneable {
- ...
- public Object clone() {
- try {
- MyClone myClone = (MyClone)super.clone();
- //分別對屬性加以克隆操作
- 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;
- }
- }
- }
深層次的克隆操作往往存在效率問題,尤其是需要讓List,Map等集合類也支持深層次的克隆操作時(shí)。
總結(jié)
本文結(jié)合范例,比較深入地介紹了Java語言的克隆屬性,以及克隆的實(shí)現(xiàn)方法等。同時(shí)分析了深層次克隆的概念,實(shí)現(xiàn),以及存在的問題等。 但是有沒有更好的方法呢?當(dāng)然,是有的,串行化來實(shí)現(xiàn)。
【編輯推薦】