明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

詳述:JAVA不妨克隆

[摘要]經(jīng)常聽(tīng)到有人說(shuō)java中沒(méi)有指針。事實(shí)如此嗎?no,java是有指針的,只不過(guò)換了個(gè)名字而已,也就是我們經(jīng)常提到的引用。我們知道,在java中一切都是對(duì)象,那么我們?nèi)绾尾倏貙?duì)象?如何在成千上萬(wàn)的對(duì)象中找到我們所需的那個(gè)對(duì)象呢?又是如何讓對(duì)象按照我們的意思來(lái)完成任務(wù)的呢?   Object o = ...
經(jīng)常聽(tīng)到有人說(shuō)java中沒(méi)有指針。事實(shí)如此嗎?no,java是有指針的,只不過(guò)換了個(gè)名字而已,也就是我們經(jīng)常提到的引用。我們知道,在java中一切都是對(duì)象,那么我們?nèi)绾尾倏貙?duì)象?如何在成千上萬(wàn)的對(duì)象中找到我們所需的那個(gè)對(duì)象呢?又是如何讓對(duì)象按照我們的意思來(lái)完成任務(wù)的呢?

  Object o = new Object();
  這是java中最常見(jiàn)的語(yǔ)句了,在這句話中做了三件事。首先聲明一個(gè)Object類型的變量o,在內(nèi)存中為對(duì)象劃分一塊地址new Object(),將聲明的變量指向內(nèi)存中的對(duì)象。如此一來(lái),我們就可以通過(guò)o來(lái)操縱對(duì)象了。就好像孩子們玩的遙控飛機(jī),在空中飛行的是飛機(jī),而使它做出優(yōu)美動(dòng)作的卻是孩子們手中的搖控器。

  "克隆"是如今聽(tīng)到的較多的詞匯,聽(tīng)說(shuō)已經(jīng)將某只羊克隆了好幾份了。但愿這種技術(shù)不要在人身上實(shí)驗(yàn)。java中也有"克隆",與現(xiàn)實(shí)世界的克隆一樣,將一個(gè)實(shí)際存在的對(duì)象拷貝幾份。如下:
  //倒霉的羊
  public class Sheep implements Cloneable{
  private String name;
  public void setName(String arg) {
  name = arg;
  }
  public String getName() {
  return name;
  }
  public Object clone() throws CloneNotSupportedException {
  return super.clone();
  }
  }
  //克隆
  public class Main {
  public static void main(String[] args) throws CloneNotSupportedException {
  Sheep sheep = new Sheep(); //先得到那只羊的實(shí)例
  sheep.setName("我是真的"); //給它做個(gè)記號(hào)
  System.out.println("sheep.getName() = " + sheep.getName());
  Sheep sheepClone = (Sheep)sheep.clone(); //開(kāi)始克隆
  System.out.println("sheepClone.getName() = " + sheepClone.getName());
  }
  }

  運(yùn)行程序結(jié)果為:
  sheep.getName() = 我是真的
  sheepClone.getName() = 我是真的

  兩只羊是一模一樣的(哪怕那只羊瘸腿)。讓我們來(lái)看看代碼。首先要注意的是Sheep類實(shí)現(xiàn)了Cloneable接口(該接口屬于java.lang包,默認(rèn)已經(jīng)導(dǎo)入了),該接口中并沒(méi)有定義要實(shí)現(xiàn)的方法,是個(gè)空接口,起標(biāo)志作用。也就是說(shuō),實(shí)現(xiàn)了這個(gè)接口的羊就不再是只普通的羊,它是一只可以被克隆的羊。再往下看,有個(gè)clone方法,返回Object類型的對(duì)象,并拋出CloneNotSupportedException異常。

  該方法覆寫(xiě)了父類(Object)的clone方法,并在最后調(diào)用了super.clone(),這也意味著無(wú)論clone類繼承結(jié)構(gòu)是什么樣的,super.clone()都會(huì)直接或間接調(diào)用Object類的clone()方法。看看jdk幫助文檔會(huì)發(fā)現(xiàn),Object類的clone()是一個(gè)native方法,我們知道,native方法的效率一般來(lái)說(shuō)都是遠(yuǎn)高于java中的非native方法。這也說(shuō)明了new一個(gè)對(duì)象,然后將原對(duì)象中的數(shù)據(jù)導(dǎo)入到新創(chuàng)建的對(duì)象中去的做法是多么愚蠢。必須說(shuō)明的是Object中的clone方法是protected的,所以要使用clone就必須繼承Object類(默認(rèn))。并且為了可以使其它類調(diào)用該方法,必須將其作用域設(shè)置為public。

  以上只是一個(gè)簡(jiǎn)單clone的實(shí)現(xiàn)。下面說(shuō)說(shuō)"影子clone"和"深度clone"。

  何為影子clone?先看一下例子。
  //倒霉的羊
  public class Sheep implements Cloneable{
  private String name;
  public void setName(String arg) {
  name = arg;
  }
  public String getName() {
  return name;
  }
  public Object clone() throws CloneNotSupportedException {
  return super.clone();
  }
  }
  //羊圈
  public class Sheepfold implements Cloneable {
  public Sheep sheep;
  public String name;
  public Sheepfold() {
  sheep = new Sheep();
  }
  public Object clone() throws CloneNotSupportedException {
  return super.clone();
  }
  }
  //克隆
  public class Main {
  public static void main(String[] args) throws Exception {
  Sheepfold fold = new Sheepfold();
  fold.name = "小羊圈";
  fold.sheep.setName("小羊");
  Sheepfold fold2 = (Sheepfold)fold.clone();
  System.out.println(" fold2.name = " + fold2.name);
  System.out.println(" fold2.sheep.getName() = " + fold2.sheep.getName());
  fold2.name = "大羊圈";
  fold2.sheep.setName("大羊");
  System.out.println("=====================================");
  System.out.println(" fold2.name = " + fold2.name);
  System.out.println("* fold2.sheep.getName() = " + fold2.sheep.getName());
  System.out.println(" fold.name = " + fold.name);
  System.out.println("* fold.sheep.getName() = " + fold.sheep.getName());
  System.out.println("=====================================");
  }
  }

  在這個(gè)例子中有三個(gè)類,Sheep和Sheepflod都實(shí)現(xiàn)了Cloneable接口,并且覆寫(xiě)了Object類的clone方法,說(shuō)明這兩個(gè)類是具有克隆能力的。注意一點(diǎn),在Sheepflod中持有一個(gè)Sheep的實(shí)例,并在Main類中對(duì)其進(jìn)行克隆,結(jié)果如下:
  fold2.name = 小羊圈
  fold2.sheep.getName() = 小羊
  =======================
  fold2.name = 大羊圈
  * fold2.sheep.getName() = 大羊
  fold.name = 小羊圈
  * fold.sheep.getName() = 大羊
  ======================

  請(qǐng)注意一下結(jié)果中帶有"*"號(hào)的兩條結(jié)果語(yǔ)句。fold2.sheep和fold.sheep的name都變?yōu)榱?大羊",很奇怪是嗎?在此之前,我們只對(duì)fold2.sheep的name賦過(guò)值。為什么fold.sheep的name也變?yōu)榱?大羊"呢?原因很簡(jiǎn)單,因?yàn)樗鼈兪侵赶蛲粋(gè)對(duì)象的不同引用。從中可以看出,調(diào)用Object類中clone()方法時(shí),首先在內(nèi)存中劃分一塊同原對(duì)象相同的空間,然后將原對(duì)象的內(nèi)容原樣拷貝至新對(duì)象。

  我們知道,java中有基本數(shù)據(jù)類型,對(duì)于基本數(shù)據(jù)類型,這樣的操作是沒(méi)有問(wèn)題的,但對(duì)非基本類型變量,它們保存的僅僅是對(duì)象的引用,這也是為什么clone后非基本類型變量和原對(duì)象中的變量指向同一個(gè)對(duì)象的原因?赡苣阋呀(jīng)注意到,程序中用到了String類型,即對(duì)象,為什么沒(méi)有出現(xiàn)引用指向同一地址的情況?

  這是因?yàn)镾tring是一個(gè)不可更改的類(immutable class),每次給它賦值時(shí),都會(huì)產(chǎn)生一個(gè)新的String對(duì)象。如String str = "a"; str += "b";在這兩句代碼中,當(dāng)執(zhí)行str += "b"時(shí),實(shí)際上是重新成生了一個(gè)值為"ab"的String對(duì)象,即重新分配了一塊內(nèi)存空間。以上clone方法通常被稱為"影子clone"。"影子clone"給我們留下了一個(gè)問(wèn)題,即多個(gè)引用指向同一個(gè)對(duì)象。如何解決該問(wèn)題呢?答案為"深度clone"。把上面的例子改成深度clone很簡(jiǎn)單,只需將Sheepfold的clone()方法改為如下即可:
  public Object clone() throws CloneNotSupportedException {
  Sheepfold fold = (Sheepfold)super.clone();
  sheep = (Sheep)fold.sheep.clone();
  return fold;
  }

  至此,clone就基本完成了。當(dāng)然,在實(shí)際使用過(guò)程中需要注意一些問(wèn)題,比如StringBuffer不可以直接clone(當(dāng)然,也有解決辦法)等等。

  全文完!




標(biāo)簽:詳述:JAVA不妨克隆