青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

woaidongmao

文章均收錄自他人博客,但不喜標(biāo)題前加-[轉(zhuǎn)貼],因其丑陋,見(jiàn)諒!~
隨筆 - 1469, 文章 - 0, 評(píng)論 - 661, 引用 - 0
數(shù)據(jù)加載中……

JAVA中的指針,引用及對(duì)象的clone

2002 12 23

Java語(yǔ)言的一個(gè)優(yōu)點(diǎn)就是取消了指針的概念,但也導(dǎo)致了許多程序員在編程中常常忽略了對(duì)象與引用的區(qū)別,本文會(huì)試圖澄清這一概念。并且由于Java不能通過(guò)簡(jiǎn)單的賦值來(lái)解決對(duì)象復(fù)制的問(wèn)題,在開(kāi)發(fā)過(guò)程中,也常常要要應(yīng)用clone()方法來(lái)復(fù)制對(duì)象。本文會(huì)讓你了解什么是影子clone與深度clone,認(rèn)識(shí)它們的區(qū)別、優(yōu)點(diǎn)及缺點(diǎn)。

 

看到這個(gè)標(biāo)題,是不是有點(diǎn)困惑:Java語(yǔ)言明確說(shuō)明取消了指針,因?yàn)橹羔樛窃趲?lái)方便的同時(shí)也是導(dǎo)致代碼不安全的根源,同時(shí)也會(huì)使程序的變得非常復(fù)雜難以理解,濫用指針寫成的代碼不亞于使用早已臭名昭著的"GOTO"語(yǔ)句。Java放棄指針的概念絕對(duì)是極其明智的。但這只是在Java語(yǔ)言中沒(méi)有明確的指針定義,實(shí)質(zhì)上每一個(gè)new語(yǔ)句返回的都是一個(gè)指針的引用,只不過(guò)在大多時(shí)候Java中不用關(guān)心如何操作這個(gè)"指針",更不用象在操作C++的指針那樣膽戰(zhàn)心驚。唯一要多多關(guān)心的是在給函數(shù)傳遞對(duì)象的時(shí)候。如下例程:

 

package reference;
class Obj{
    String str = "init value";
    public String toString(){
        return str;
    }
}
public class ObjRef{
    Obj aObj = new Obj(); //定義時(shí)候即new
    int aInt = 11;
    public void changeObj(Obj inObj){
        inObj.str = "changed value";
    }
    public void changePri(int inInt){
        inInt = 22;
    }
    public static void main(String[] args) 
    {
        ObjRef oRef = new ObjRef();
        System.out.println("Before call changeObj() method: " + oRef.aObj);
        oRef.changeObj(oRef.aObj);//---- 對(duì)象,傳引用
        System.out.println("After call changeObj() method: " + oRef.aObj);
        System.out.println("==================Print Primtive=================");
        System.out.println("Before call changePri() method: " + oRef.aInt);
        oRef.changePri(oRef.aInt);//---- 內(nèi)置類型,傳值
        System.out.println("After call changePri() method: " + oRef.aInt);
    }
}
/* RUN RESULT
Before call changeObj() method: init value
After call changeObj() method: changed value
==================Print Primtive=================
Before call changePri() method: 11
After call changePri() method: 11
*
*/

 

這段代碼的主要部分調(diào)用了兩個(gè)很相近的方法,changeObj()changePri()。唯一不同的是它們一個(gè)把對(duì)象作為輸入?yún)?shù),另一個(gè)把Java中的基本類型int作為輸入?yún)?shù)。并且在這兩個(gè)函數(shù)體內(nèi)部都對(duì)輸入的參數(shù)進(jìn)行了改動(dòng)。看似一樣的方法,程序輸出的結(jié)果卻不太一樣。changeObj()方法真正的把輸入的參數(shù)改變了,而changePri()方法對(duì)輸入的參數(shù)沒(méi)有任何的改變。

 

從這個(gè)例子知道Java對(duì)對(duì)象和基本的數(shù)據(jù)類型的處理是不一樣的。C語(yǔ)言一樣,當(dāng)把Java的基本數(shù)據(jù)類型(如intchardouble等)作為入口參數(shù)傳給函數(shù)體的時(shí)候,傳入的參數(shù)在函數(shù)體內(nèi)部變成了局部變量,這個(gè)局部變量是輸入?yún)?shù)的一個(gè)拷貝,所有的函數(shù)體內(nèi)部的操作都是針對(duì)這個(gè)拷貝的操作,函數(shù)執(zhí)行結(jié)束后,這個(gè)局部變量也就完成了它的使命,它影響不到作為輸入?yún)?shù)的變量。這種方式的參數(shù)傳遞被稱為"值傳遞"而在Java中用對(duì)象的作為入口參數(shù)的傳遞則缺省為"引用傳遞",也就是說(shuō)僅僅傳遞了對(duì)象的一個(gè)"引用",這個(gè)"引用"的概念同C語(yǔ)言中的指針引用是一樣的。當(dāng)函數(shù)體內(nèi)部對(duì)輸入變量改變時(shí),實(shí)質(zhì)上就是在對(duì)這個(gè)對(duì)象的直接操作。

 

除了在函數(shù)傳值的時(shí)候是"引用傳遞",在任何用""向?qū)ο笞兞抠x值的時(shí)候都是"引用傳遞"。如:

package reference;
class PassObj
{
    String str = "init value";
}
public class ObjPassValue 
{
    public static void main(String[] args) 
    {
        PassObj objA = new PassObj();
        PassObj objB = objA;
        objA.str = "changed in objA";
        System.out.println("Print objB.str value: " + objB.str);
    }
}
/* RUN RESULT
Print objB.str value: changed in objA
*/

 

第一句是在內(nèi)存中生成一個(gè)新的PassObj對(duì)象,然后把這個(gè)PassObj的引用賦給變量objA,第二句是把PassObj對(duì)象的引用又賦給了變量objB。此時(shí)objAobjB是兩個(gè)完全一致的變量,以后任何對(duì)objA的改變都等同于對(duì)objB的改變。

即使明白了Java語(yǔ)言中的"指針"概念也許還會(huì)不經(jīng)意間犯下面的錯(cuò)誤。

 

l     Hashtable真的能存儲(chǔ)對(duì)象嗎?

看一看下面的很簡(jiǎn)單的代碼,先是聲明了一個(gè)HashtableStringBuffer對(duì)象,然后分四次把StriingBuffer對(duì)象放入到Hashtable表中,在每次放入之前都對(duì)這個(gè)StringBuffer對(duì)象append()了一些新的字符串:

package reference;
import java.util.*;
public class HashtableAdd{
    public static void main(String[] args){
        Hashtable ht = new Hashtable();
        StringBuffer sb = new StringBuffer();
        sb.append("abc,");
        ht.put("1",sb);     
        sb.append("def,");
        ht.put("2",sb);
        sb.append("mno,");
        ht.put("3",sb);
        sb.append("xyz.");
        ht.put("4",sb);
        
        int numObj=0;
        Enumeration it = ht.elements();
        while(it.hasMoreElements()){
            System.out.print("get StringBufffer "+(++numObj)+" from Hashtable: ");
            System.out.println(it.nextElement());
        }
    }
}

 

如果你認(rèn)為輸出的結(jié)果是:
get StringBufffer 1 from Hashtable: abc,
get StringBufffer 2 from Hashtable: abc,def

get StringBufffer 3 from Hashtable: abc,def,mno,
get StringBufffer 4 from Hashtable: abc,def,mno,xyz.

 

那么你就要回過(guò)頭再仔細(xì)看一看上一個(gè)問(wèn)題了,把對(duì)象時(shí)作為入口參數(shù)傳給函數(shù),實(shí)質(zhì)上是傳遞了對(duì)象的引用,向Hashtable傳遞StringBuffer對(duì)象也是只傳遞了這個(gè)StringBuffer對(duì)象的引用!每一次向Hashtable表中put一次StringBuffer,并沒(méi)有生成新的StringBuffer對(duì)象,只是在Hashtable表中又放入了一個(gè)指向同一StringBuffer對(duì)象的引用而已

 

對(duì)Hashtable表存儲(chǔ)的任何一個(gè)StringBuffer對(duì)象(更確切的說(shuō)應(yīng)該是對(duì)象的引用)的改動(dòng),實(shí)際上都是對(duì)同一個(gè)"StringBuffer"的改動(dòng)。所以Hashtable并不能真正存儲(chǔ)能對(duì)象,而只能存儲(chǔ)對(duì)象的引用。也應(yīng)該知道這條原則對(duì)與Hashtable相似的Vector, List, Map, Set等都是一樣的。

 

上面的例程的實(shí)際輸出的結(jié)果是:

/* RUN RESULT
get StringBufffer 1 from Hashtable: abc,def,mno,xyz.
get StringBufffer 2 from Hashtable: abc,def,mno,xyz.
get StringBufffer 3 from Hashtable: abc,def,mno,xyz.
get StringBufffer 4 from Hashtable: abc,def,mno,xyz.
*/

 

l     類,對(duì)象與引用

Java最基本的概念就是類,類包括函數(shù)和變量。如果想要應(yīng)用類,就要把類生成對(duì)象,這個(gè)過(guò)程被稱作"類的實(shí)例化"。有幾種方法把類實(shí)例化成對(duì)象,最常用的就是用"new"操作符類實(shí)例化成對(duì)象后,就意味著要在內(nèi)存中占據(jù)一塊空間存放實(shí)例。想要對(duì)這塊空間操作就要應(yīng)用到對(duì)象的引用。引用在Java語(yǔ)言中的體現(xiàn)就是變量,而變量的類型就是這個(gè)引用的對(duì)象。雖然在語(yǔ)法上可以在生成一個(gè)對(duì)象后直接調(diào)用該對(duì)象的函數(shù)或變量,如:

new String("Hello NDP")).substring(0,3)  //RETURN RESULT: Hel

 

但由于沒(méi)有相應(yīng)的引用,對(duì)這個(gè)對(duì)象的使用也只能局限這條語(yǔ)句中了。(回立即銷毀嗎??jī)?nèi)存回收機(jī)制是不是使用了引用計(jì)數(shù)的方式呢?

  1. 產(chǎn)生:引用總是在把對(duì)象作參數(shù)"傳遞"的過(guò)程中自動(dòng)發(fā)生,不需要人為的產(chǎn)生,也不能人為的控制引用的產(chǎn)生。這個(gè)傳遞包括把對(duì)象作為函數(shù)的入口參數(shù)的情況,也包括用""進(jìn)行對(duì)象賦值的時(shí)候
  2. 范圍:只有局部的引用,沒(méi)有局部的對(duì)象。引用在Java語(yǔ)言的體現(xiàn)就是變量,而變量在Java語(yǔ)言中是有范圍的,可以是局部的,也可以是全局的。
  3. 生存期:程序只能控制引用的生存周期。對(duì)象的生存期是由Java控制。用"new Object()"語(yǔ)句生成一個(gè)新的對(duì)象,是在計(jì)算機(jī)的內(nèi)存中聲明一塊區(qū)域存儲(chǔ)對(duì)象,只有Java的垃圾收集器才能決定在適當(dāng)?shù)臅r(shí)候回收對(duì)象占用的內(nèi)存
  4. 沒(méi)有辦法阻止對(duì)引用的改動(dòng)。

 

l     什么是"clone"

在實(shí)際編程過(guò)程中,我們常常要遇到這種情況:有一個(gè)對(duì)象A,在某一時(shí)刻A中已經(jīng)包含了一些有效值,此時(shí)可能會(huì)需要一個(gè)和A完全相同新對(duì)象B,并且此后對(duì)B任何改動(dòng)都不會(huì)影響到A中的值,也就是說(shuō),AB是兩個(gè)獨(dú)立的對(duì)象,但B的初始值是由A對(duì)象確定的。在Java語(yǔ)言中,用簡(jiǎn)單的賦值語(yǔ)句是不能滿足這種需求的。要滿足這種需求雖然有很多途徑,但實(shí)現(xiàn)clone()方法是其中最簡(jiǎn)單,也是最高效的手段。

 

Java的所有類都默認(rèn)繼承java.lang.Object類,在java.lang.Object類中有一個(gè)方法clone()JDK API的說(shuō)明文檔解釋這個(gè)方法將返回Object對(duì)象的一個(gè)拷貝。要說(shuō)明的有兩點(diǎn):一是拷貝對(duì)象返回的是一個(gè)新對(duì)象,而不是一個(gè)引用。二是拷貝對(duì)象與用new操作符返回的新對(duì)象的區(qū)別就是這個(gè)拷貝已經(jīng)包含了一些原來(lái)對(duì)象的信息,而不是對(duì)象的初始信息。

 

l     怎樣應(yīng)用clone()方法?

 

一個(gè)很典型的調(diào)用clone()代碼如下:

class CloneClass implements Cloneable{
    public int aInt;
    public Object clone(){
        CloneClass o = null;
        try{
            o = (CloneClass)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return o;
    }

 

有三個(gè)值得注意的地方,一是希望能實(shí)現(xiàn)clone功能的CloneClass類實(shí)現(xiàn)了Cloneable接口,這個(gè)接口屬于java.lang包,java.lang包已經(jīng)被缺省的導(dǎo)入類中,所以不需要寫成java.lang.Cloneable。另一個(gè)值得請(qǐng)注意的是重載了clone()方法。最后在clone()方法中調(diào)用了super.clone()這也意味著無(wú)論clone類的繼承結(jié)構(gòu)是什么樣的,super.clone()直接或間接調(diào)用了java.lang.Object類的clone()方法。下面再詳細(xì)的解釋一下這幾點(diǎn)。

 

應(yīng)該說(shuō)第三點(diǎn)是最重要的,仔細(xì)觀察一下Object類的clone()一個(gè)native方法,native方法的效率一般來(lái)說(shuō)都是遠(yuǎn)高于java中的非native方法。這也解釋了為什么要用Objectclone()方法而不是先new一個(gè)類,然后把原始對(duì)象中的信息賦到新對(duì)象中,雖然這也實(shí)現(xiàn)了clone功能。對(duì)于第二點(diǎn),也要觀察Object類中的clone()還是一個(gè)protected屬性的方法。這也意味著如果要應(yīng)用clone()方法,必須繼承Object類,在Java中所有的類是缺省繼承Object類的,也就不用關(guān)心這點(diǎn)了。然后重載clone()方法。還有一點(diǎn)要考慮的是為了讓其它類能調(diào)用這個(gè)clone類的clone()方法,重載之后要把clone()方法的屬性設(shè)置為public

 

那么clone類為什么還要實(shí)現(xiàn)Cloneable接口呢?稍微注意一下,Cloneable接口是不包含任何方法的!其實(shí)這個(gè)接口僅僅是一個(gè)標(biāo)志,而且這個(gè)標(biāo)志也僅僅是針對(duì)Object類中clone()方法的,如果clone類沒(méi)有實(shí)現(xiàn)Cloneable接口,并調(diào)用了Objectclone()方法(也就是調(diào)用了super.Clone()方法),那么Objectclone()方法就會(huì)拋出CloneNotSupportedException異常。

 

以上是clone的最基本的步驟,想要完成一個(gè)成功的clone,還要了解什么是"影子clone""深度clone"

 

l     什么是影子clone

下面的例子包含三個(gè)類UnCloneACloneBCloneMainCloneB類包含了一個(gè)UnCloneA的實(shí)例和一個(gè)int類型變量,并且重載clone()方法。CloneMain類初始化UnCloneA類的一個(gè)實(shí)例b1,然后調(diào)用clone()方法生成了一個(gè)b1的拷貝b2。最后考察一下b1b2的輸出:

 

package clone;
class UnCloneA {
    private int i;
    public UnCloneA(int ii) { i = ii; }
    public void doubleValue() { i *= 2; }
    public String toString() {
        return Integer.toString(i);
    }
}
class CloneB implements Cloneable{
    public int aInt;
    public UnCloneA unCA = new UnCloneA(111);
    public Object clone(){
        CloneB o = null;
        try{
            o = (CloneB)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return o;
    }
}
public class CloneMain {
    public static void main(String[] a){
        CloneB b1 = new CloneB();
        b1.aInt = 11;
        System.out.println("before clone,b1.aInt = "+ b1.aInt);
        System.out.println("before clone,b1.unCA = "+ b1.unCA);
                
        CloneB b2 = (CloneB)b1.clone();
        b2.aInt = 22;
        b2.unCA.doubleValue();
        System.out.println("=================================");
        System.out.println("after clone,b1.aInt = "+ b1.aInt);
        System.out.println("after clone,b1.unCA = "+ b1.unCA);
        System.out.println("=================================");
        System.out.println("after clone,b2.aInt = "+ b2.aInt);
        System.out.println("after clone,b2.unCA = "+ b2.unCA);
    }
}
/** RUN RESULT:
before clone,b1.aInt = 11
before clone,b1.unCA = 111
=================================
after clone,b1.aInt = 11
after clone,b1.unCA = 222
=================================
after clone,b2.aInt = 22
after clone,b2.unCA = 222
*/

 

輸出的結(jié)果說(shuō)明int類型的變量aIntUnCloneA的實(shí)例對(duì)象unCAclone結(jié)果不一致,int類型是真正的被clone了,因?yàn)楦淖兞?span lang="EN-US">b2中的aInt變量,對(duì)b1aInt沒(méi)有產(chǎn)生影響,也就是說(shuō),b2.aIntb1.aInt已經(jīng)占據(jù)了不同的內(nèi)存空間,b2.aIntb1.aInt的一個(gè)真正拷貝。相反,對(duì)b2.unCA的改變同時(shí)改變了b1.unCA,很明顯,b2.unCAb1.unCA是僅僅指向同一個(gè)對(duì)象的不同引用!從中可以看出,調(diào)用Object類中clone()方法產(chǎn)生的效果是:先在內(nèi)存中開(kāi)辟一塊和原始對(duì)象一樣的空間,然后原樣拷貝原始對(duì)象中的內(nèi)容。對(duì)基本數(shù)據(jù)類型,這樣的操作是沒(méi)有問(wèn)題的,但對(duì)非基本類型變量,我們知道它們保存的僅僅是對(duì)象的引用,這也導(dǎo)致clone后的非基本類型變量和原始對(duì)象中相應(yīng)的變量指向的是同一個(gè)對(duì)象。

 

大多時(shí)候,這種clone的結(jié)果往往不是我們所希望的結(jié)果,這種clone也被稱為"影子clone"。要想讓b2.unCA指向與b2.unCA不同的對(duì)象,而且b2.unCA中還要包含b1.unCA中的信息作為初始信息,就要實(shí)現(xiàn)深度clone

 

l     怎么進(jìn)行深度clone

把上面的例子改成深度clone很簡(jiǎn)單,需要兩個(gè)改變:一是讓UnCloneA類也實(shí)現(xiàn)和CloneB類一樣的clone功能(實(shí)現(xiàn)Cloneable接口,重載clone()方法)。二是在CloneBclone()方法中加入一句o.unCA = (UnCloneA)unCA.clone();

程序如下:

package clone.ext;
class UnCloneA implements Cloneable{
    private int i;
    public UnCloneA(int ii) { i = ii; }
    public void doubleValue() { i *= 2; }
    public String toString() {
        return Integer.toString(i);
    }
    public Object clone(){
        UnCloneA o = null;
        try{
            o = (UnCloneA)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return o;
    }
}
class CloneB implements Cloneable{
    public int aInt;
    public UnCloneA unCA = new UnCloneA(111);
    public Object clone(){
        CloneB o = null;
        try{
            o = (CloneB)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        o.unCA = (UnCloneA)unCA.clone();
        return o;
    }
}
public class CloneMain {
    public static void main(String[] a){
        CloneB b1 = new CloneB();
        b1.aInt = 11;
        System.out.println("before clone,b1.aInt = "+ b1.aInt);
        System.out.println("before clone,b1.unCA = "+ b1.unCA);
                
        CloneB b2 = (CloneB)b1.clone();
        b2.aInt = 22;
        b2.unCA.doubleValue();
        System.out.println("=================================");
        System.out.println("after clone,b1.aInt = "+ b1.aInt);
        System.out.println("after clone,b1.unCA = "+ b1.unCA);
        System.out.println("=================================");
        System.out.println("after clone,b2.aInt = "+ b2.aInt);
        System.out.println("after clone,b2.unCA = "+ b2.unCA);
    }
}
/** RUN RESULT:
before clone,b1.aInt = 11
before clone,b1.unCA = 111
=================================
after clone,b1.aInt = 11
after clone,b1.unCA = 111
=================================
after clone,b2.aInt = 22
after clone,b2.unCA = 222
*/

 

可以看出,現(xiàn)在b2.unCA的改變對(duì)b1.unCA沒(méi)有產(chǎn)生影響。此時(shí)b1.unCAb2.unCA指向了兩個(gè)不同的UnCloneA實(shí)例,而且在CloneB b2 = (CloneB)b1.clone();調(diào)用的那一刻b1b2擁有相同的值,在這里,b1.i = b2.i = 11

 

要知道不是所有的類都能實(shí)現(xiàn)深度clone的。例如,如果把上面的CloneB類中的UnCloneA類型變量改成StringBuffer類型,看一下JDK API中關(guān)于StringBuffer的說(shuō)明StringBuffer沒(méi)有重載clone()方法,更為嚴(yán)重的是StringBuffer還是一個(gè)final,這也是說(shuō)我們也不能用繼承的辦法間接實(shí)現(xiàn)StringBufferclone。如果一個(gè)類中包含有StringBuffer類型對(duì)象或和StringBuffer相似類的對(duì)象,我們有兩種選擇:要么只能實(shí)現(xiàn)影子clone,要么就在類的clone()方法中加一句(假設(shè)是SringBuffer對(duì)象,而且變量名仍是unCA): o.unCA = new StringBuffer(unCA.toString()); //原來(lái)的是:o.unCA = (UnCloneA)unCA.clone();

 

還要知道的是除了基本數(shù)據(jù)類型能自動(dòng)實(shí)現(xiàn)深度clone以外,String對(duì)象是一個(gè)例外,它clone后的表現(xiàn)好象也實(shí)現(xiàn)了深度clone,雖然這只是一個(gè)假象,但卻大大方便了我們的編程。

 

l     CloneStringStringBuffer的區(qū)別

應(yīng)該說(shuō)明的是,這里不是著重說(shuō)明StringStringBuffer的區(qū)別,但從這個(gè)例子里也能看出String類的一些與眾不同的地方。

 

下面的例子中包括兩個(gè)類,CloneC類包含一個(gè)String類型變量和一個(gè)StringBuffer類型變量,并且實(shí)現(xiàn)了clone()方法。在StrClone類中聲明了CloneC類型變量c1,然后調(diào)用c1clone()方法生成c1的拷貝c2,在對(duì)c2中的StringStringBuffer類型變量用相應(yīng)的方法改動(dòng)之后打印結(jié)果:

package clone;
class CloneC implements Cloneable{
    public String str;
    public StringBuffer strBuff;
    public Object clone(){
        CloneC o = null;
        try{
            o = (CloneC)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return o;
    }
    
}
public class StrClone {
    public static void main(String[] a){
        CloneC c1 = new CloneC();
        c1.str = new String("initializeStr");
        c1.strBuff = new StringBuffer("initializeStrBuff");
        System.out.println("before clone,c1.str = "+ c1.str);
        System.out.println("before clone,c1.strBuff = "+ c1.strBuff);
                
        CloneC c2 = (CloneC)c1.clone();
        c2.str = c2.str.substring(0,5);
        c2.strBuff = c2.strBuff.append(" change strBuff clone");
        System.out.println("=================================");
        System.out.println("after clone,c1.str = "+ c1.str);
        System.out.println("after clone,c1.strBuff = "+ c1.strBuff);
        System.out.println("=================================");
        System.out.println("after clone,c2.str = "+ c2.str);
        System.out.println("after clone,c2.strBuff = "+ c2.strBuff);
    }
}
/* RUN RESULT
before clone,c1.str = initializeStr
before clone,c1.strBuff = initializeStrBuff
=================================
after clone,c1.str = initializeStr
after clone,c1.strBuff = initializeStrBuff change strBuff clone
=================================
after clone,c2.str = initi
after clone,c2.strBuff = initializeStrBuff change strBuff clone
*
*/

 

打印的結(jié)果可以看出,String類型的變量好象已經(jīng)實(shí)現(xiàn)了深度clone,因?yàn)閷?duì)c2.str的改動(dòng)并沒(méi)有影響到c1.str難道JavaSring類看成了基本數(shù)據(jù)類型?其實(shí)不然,這里有一個(gè)小小的把戲,秘密就在于c2.str = c2.str.substring(0,5)這一語(yǔ)句!實(shí)質(zhì)上,在clone的時(shí)候c1.strc2.str仍然是引用,而且都指向了同一個(gè)String對(duì)象。但在執(zhí)行c2.str = c2.str.substring(0,5)的時(shí)候,它作用相當(dāng)于生成了一個(gè)新的String類型,然后又賦回給c2.str這是因?yàn)?span lang="EN-US">StringSun公司的工程師寫成了一個(gè)不可更改的類(immutable class),在所有String類中的函數(shù)都不能更改自身的值。下面給出很簡(jiǎn)單的一個(gè)例子:

 

package clone;

public class StrTest { public static void main(String[] args) {

String str1 = "This is a test for immutable";

String str2 = str1.substring(0,8);

System.out.println("print str1 : " + str1);

System.out.println("print str2 : " + str2); }

 } /* RUN RESULT print str1 : This is a test for immutable print str2 : This is */

 

例子中,雖然str1調(diào)用了substring()方法,但str1的值并沒(méi)有改變。類似的,String類中的其它方法也是如此。當(dāng)然如果我們把最上面的例子中的這兩條語(yǔ)句

 

c2.str = c2.str.substring(0,5);
c2.strBuff = c2.strBuff.append(" change strBuff clone");
       

 

改成下面這樣:

c2.str.substring(0,5);
c2.strBuff.append(" change strBuff clone");

 

去掉了重新賦值的過(guò)程,c2.str也就不能有變化了,我們的把戲也就露餡了。但在編程過(guò)程中只調(diào)用

c2.str.substring(0,5);

 

語(yǔ)句是沒(méi)有任何意義的。

應(yīng)該知道的是在Java中所有的基本數(shù)據(jù)類型都有一個(gè)相對(duì)應(yīng)的類,Integer類對(duì)應(yīng)int類型Double類對(duì)應(yīng)double類型等等,這些類也String類相同,都是不可以改變的類。也就是說(shuō),這些的類中的所有方法都是不能改變其自身的值的。這也讓我們?cè)诰?span lang="EN-US">clone類的時(shí)候有了一個(gè)更多的選擇。同時(shí)我們也可以把自己的類編成不可更改的類。

 

關(guān)于作者

倪大鵬,有五年的軟件開(kāi)發(fā)經(jīng)驗(yàn),其中的近四年時(shí)間里是在從事與Java相關(guān)技術(shù)的應(yīng)用與開(kāi)發(fā)。你可以通過(guò)e-mail: ndp@21cn.com與他聯(lián)系。

posted on 2009-07-24 13:17 肥仔 閱讀(239) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Web-后臺(tái)

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            在线一区免费观看| 亚洲一区二区三区涩| 久久国产精品一区二区三区四区| 久久亚洲综合色| 久久久久国色av免费观看性色| 日韩一级不卡| 这里只有精品在线播放| 亚洲美洲欧洲综合国产一区| 最新日韩欧美| 亚洲欧美日韩视频二区| 午夜精品www| 久久影院午夜片一区| 欧美激情在线观看| 日韩视频欧美视频| 香蕉久久夜色精品| 欧美不卡视频一区发布| 欧美午夜精彩| 影音国产精品| 亚洲欧美另类久久久精品2019| 久久国产精品亚洲va麻豆| 蜜臀av国产精品久久久久| 中文欧美字幕免费| 乱中年女人伦av一区二区| 欧美亚洲第一区| 亚洲国产综合91精品麻豆| 国产精品久久久久999| 亚洲精华国产欧美| 久久中文久久字幕| 亚洲经典视频在线观看| 亚洲欧美日韩精品一区二区| 免费看的黄色欧美网站| 亚洲一区二区三区久久| 欧美日本亚洲韩国国产| 亚洲三级视频在线观看| 欧美成人日韩| 欧美—级在线免费片| …久久精品99久久香蕉国产| 久久精品二区三区| 欧美亚洲在线视频| 伊人成人开心激情综合网| 欧美成人中文字幕| 欧美视频三区在线播放| 亚洲欧美激情一区二区| 午夜视频一区在线观看| 国产视频久久网| 欧美88av| 国产精品国产三级国产普通话99 | 久久综合久色欧美综合狠狠| 国产日产亚洲精品| 亚洲国产欧美国产综合一区| 欧美精品1区2区| 久久精品国产久精国产思思| 欧美www视频| 久久久久久久久综合| 欧美日韩国产精品一区| 蜜臀久久久99精品久久久久久| 欧美日韩国产bt| 久久久久国产精品午夜一区| 欧美影院在线| 欧美大片在线观看一区| 久久精品青青大伊人av| 欧美精品一区二区蜜臀亚洲| 欧美伊人久久久久久久久影院| 欧美v亚洲v综合ⅴ国产v| 欧美与欧洲交xxxx免费观看| 欧美日韩国内自拍| 欧美国产综合视频| 在线免费日韩片| 久久精品中文字幕免费mv| 欧美一区二视频在线免费观看| 蜜臀久久99精品久久久久久9| 亚洲欧美日韩一区在线观看| 欧美日韩亚洲高清| 亚洲三级影片| 日韩午夜免费视频| 欧美劲爆第一页| 亚洲精品一区二区三区在线观看| 亚洲高清二区| 欧美日韩精品一二三区| 亚洲精品乱码久久久久久久久| 亚洲精品中文字幕女同| 亚洲一区二区成人| 国产精品久久中文| 久久久国产精品一区| 欧美激情小视频| 99这里只有精品| 国产一区在线免费观看| 久久婷婷久久| 在线视频免费在线观看一区二区| 亚洲一区二区欧美| 亚洲国产精品va在线看黑人动漫| 免费视频一区二区三区在线观看| 日韩视频免费在线观看| 久久久亚洲精品一区二区三区| 亚洲国产精品欧美一二99| 欧美激情精品久久久久久变态| 国产揄拍国内精品对白| 在线综合亚洲| 亚洲精选大片| 国产一区二区日韩精品欧美精品| 乱人伦精品视频在线观看| 亚洲一二三级电影| 亚洲娇小video精品| 久久在精品线影院精品国产| 亚洲一区激情| 在线视频一区观看| 99国产精品视频免费观看| 亚洲高清不卡在线| 狠狠色综合色区| 激情丁香综合| 亚洲成色精品| 亚洲人成网站777色婷婷| 国产在线不卡精品| 红桃视频国产一区| 亚洲丶国产丶欧美一区二区三区| 国产精品视频1区| 国产精品一卡二| 好看的日韩视频| 亚洲成人资源| 这里只有精品视频| 欧美影院在线播放| 欧美+日本+国产+在线a∨观看| 欧美不卡高清| 亚洲欧美999| 欧美电影免费观看高清| 国产精品久久97| 在线观看不卡| 午夜天堂精品久久久久| 欧美1级日本1级| 先锋影音久久| 欧美无砖砖区免费| 亚洲第一精品夜夜躁人人爽| 亚洲网站在线看| 亚洲第一色在线| 久久精品1区| 国产精品日韩电影| 亚洲精选成人| 欧美日韩和欧美的一区二区| 国产综合欧美| 久久gogo国模裸体人体| 在线视频日韩| 欧美日韩情趣电影| 亚洲缚视频在线观看| 久久精品论坛| 亚洲影音先锋| 国产日韩一区二区| 久久国产精品黑丝| 欧美一区二区免费观在线| 国产精品国产| 欧美一二三区精品| 性色av香蕉一区二区| 国内精品久久国产| 欧美高清在线一区| 欧美成人三级在线| 99视频精品免费观看| 日韩视频在线一区二区三区| 国产精品v日韩精品v欧美精品网站| 一区二区三区免费看| 在线综合亚洲欧美在线视频| 国产精品影院在线观看| 欧美成人精品一区二区| 欧美高清日韩| 欧美一区二区三区在线看| 久久久久久久久久码影片| 一区二区毛片| 久久精品欧美日韩| 一区二区三区色| 久久精品国产精品| 亚洲男人的天堂在线观看 | 欧美在线日韩| 欧美精品久久天天躁| 亚洲欧美一区二区三区久久| 麻豆精品视频在线观看视频| 亚洲在线播放电影| 欧美a级一区| 美女91精品| 国内精品久久久久久久97牛牛| 亚洲欧洲另类| 亚洲激情av| 欧美影院在线| 久久久久久久综合狠狠综合| 久久女同精品一区二区| 欧美日韩在线播放三区四区| 亚洲第一黄色网| 亚洲第一区在线| 亚洲欧美日韩国产精品| 一本久久青青| 欧美日韩免费一区二区三区| 欧美激情精品久久久久久久变态| 国产亚洲激情在线| 久久久久国产免费免费| 欧美成人国产一区二区| 午夜精品国产更新| 欧美自拍偷拍午夜视频| 国产日韩欧美中文| 久久久人成影片一区二区三区观看 | 国产精品美女久久久久久2018| 亚洲国产日韩一区| 宅男噜噜噜66国产日韩在线观看| 欧美黄在线观看|