java 中对象引用,以及对象赋值
Java對(duì)象及其引用
Java對(duì)象及引用是容易混淆卻又必須掌握的基礎(chǔ)知識(shí),本章闡述Java對(duì)象和引用的概念,以及與其密切相關(guān)的參數(shù)傳遞。
在許多Java書(shū)中,把對(duì)象和對(duì)象的引用混為一談。可是,如果我分不清對(duì)象與對(duì)象引用,那實(shí)在沒(méi)法很好地理解下面的面向?qū)ο蠹夹g(shù)。把自己的一點(diǎn)認(rèn)識(shí)寫下來(lái),或許能讓初學(xué)Java的朋友們少走一點(diǎn)彎路。
???????為便于說(shuō)明,我們先定義一個(gè)簡(jiǎn)單的類:
?????? class Vehicle {
?????? int passengers;??????
?????? int fuelcap;
?????? int mpg;
?????????????????? }
有了這個(gè)模板,就可以用它來(lái)創(chuàng)建對(duì)象:
?????? Vehicle veh1 = new Vehicle();
通常把這條語(yǔ)句的動(dòng)作稱之為創(chuàng)建一個(gè)對(duì)象,其實(shí),它包含了四個(gè)動(dòng)作。
1)右邊的“new Vehicle”,是以Vehicle類為模板,在堆空間里創(chuàng)建一個(gè)Vehicle類對(duì)象(也簡(jiǎn)稱為Vehicle對(duì)象)。
2)末尾的()意味著,在對(duì)象創(chuàng)建后,立即調(diào)用Vehicle類的構(gòu)造函數(shù),對(duì)剛生成的對(duì)象進(jìn)行初始化。構(gòu)造函數(shù)是肯定有的。如果你沒(méi)寫,Java會(huì)給你補(bǔ)上一個(gè)默認(rèn)的構(gòu)造函數(shù)。
3)左邊的“Vehicle veh?1”創(chuàng)建了一個(gè)Vehicle類引用變量。所謂Vehicle類引用,就是以后可以用來(lái)指向Vehicle對(duì)象的對(duì)象引用。
4)“=”操作符使對(duì)象引用指向剛創(chuàng)建的那個(gè)Vehicle對(duì)象。
我們可以把這條語(yǔ)句拆成兩部分:
Vehicle veh1;
veh1 = new Vehicle();
效果是一樣的。這樣寫,就比較清楚了,有兩個(gè)實(shí)體:一是對(duì)象引用變量,一是對(duì)象本身。
???????在堆空間里創(chuàng)建的實(shí)體,與在數(shù)據(jù)段以及棧空間里創(chuàng)建的實(shí)體不同。盡管它們也是確確實(shí)實(shí)存在的實(shí)體,但是,我們看不見(jiàn),也摸不著。不僅如此,
???????我們仔細(xì)研究一下第二句,找找剛創(chuàng)建的對(duì)象叫什么名字?有人說(shuō),它叫“Vehicle”。不對(duì),“Vehicle”是類(對(duì)象的創(chuàng)建模板)的名字。
???????一個(gè)Vehicle類可以據(jù)此創(chuàng)建出無(wú)數(shù)個(gè)對(duì)象,這些對(duì)象不可能全叫“Vehicle”。
???????對(duì)象連名都沒(méi)有,沒(méi)法直接訪問(wèn)它。我們只能通過(guò)對(duì)象引用來(lái)間接訪問(wèn)對(duì)象。
???????為了形象地說(shuō)明對(duì)象、引用及它們之間的關(guān)系,可以做一個(gè)或許不很妥當(dāng)?shù)谋扔鳌?duì)象好比是一只很大的氣球,大到我們抓不住它。引用變量是一根繩, 可以用來(lái)系汽球。
???????如果只執(zhí)行了第一條語(yǔ)句,還沒(méi)執(zhí)行第二條,此時(shí)創(chuàng)建的引用變量veh1還沒(méi)指向任何一個(gè)對(duì)象,它的值是null。引用變量可以指向某個(gè)對(duì)象,或者為null。
???????它是一根繩,一根還沒(méi)有系上任何一個(gè)汽球的繩。執(zhí)行了第二句后,一只新汽球做出來(lái)了,并被系在veh1這根繩上。我們抓住這根繩,就等于抓住了那只汽球。
???????再來(lái)一句:
?????? Vehicle veh2;
就又做了一根繩,還沒(méi)系上汽球。如果再加一句:
?????? veh2 = veh1;
系上了。這里,發(fā)生了復(fù)制行為。但是,要說(shuō)明的是,對(duì)象本身并沒(méi)有被復(fù)制,被復(fù)制的只是對(duì)象引用。結(jié)果是,veh2也指向了veh1所指向的對(duì)象。兩根繩系的是同一只汽球。
???????如果用下句再創(chuàng)建一個(gè)對(duì)象:
veh2 = new Vehicle();
則引用變量veh2改指向第二個(gè)對(duì)象。
???????從以上敘述再推演下去,我們可以獲得以下結(jié)論:
(1)一個(gè)對(duì)象引用可以指向0個(gè)或1個(gè)對(duì)象(一根繩子可以不系汽球,也可以系一個(gè)汽球);
(2)一個(gè)對(duì)象可以有N個(gè)引用指向它(可以有N條繩子系住一個(gè)汽球)。
???????如果再來(lái)下面語(yǔ)句:
?????? veh1 = veh2;
按上面的推斷,veh1也指向了第二個(gè)對(duì)象。這個(gè)沒(méi)問(wèn)題。問(wèn)題是第一個(gè)對(duì)象呢?沒(méi)有一條繩子系住它,它飛了。多數(shù)書(shū)里說(shuō),它被Java的垃圾回收機(jī)制回收了。
這不確切。正確地說(shuō),它已成為垃圾回收機(jī)制的處理對(duì)象。至于什么時(shí)候真正被回收,那要看垃圾回收機(jī)制的心情了。
???????由此看來(lái),下面的語(yǔ)句應(yīng)該不合法吧?至少是沒(méi)用的吧?
new Vehicle();
不對(duì)。它是合法的,而且可用的。譬如,如果我們僅僅為了打印而生成一個(gè)對(duì)象,就不需要用引用變量來(lái)系住它。最常見(jiàn)的就是打印字符串:
????System.out.println(“I am Java!”);
字符串對(duì)象“I am Java!”在打印后即被丟棄。有人把這種對(duì)象稱之為臨時(shí)對(duì)象。
???????對(duì)象與引用的關(guān)系將持續(xù)到對(duì)象回收。
??
先看下面的程序:
StringBuffer s;
s = new StringBuffer("Hello World!");
第一個(gè)語(yǔ)句僅為引用(reference)分配了空間,而第二個(gè)語(yǔ)句則通過(guò)調(diào)用類(StringBuffer)的構(gòu)造函數(shù)StringBuffer(String str)為類生成了一個(gè)實(shí)例(或稱為對(duì)象)。這兩個(gè)操作被完成后,對(duì)象的內(nèi)容則可通過(guò)s進(jìn)行訪問(wèn)——在Java里都是通過(guò)引用來(lái)操縱對(duì)象的。
?
Java對(duì)象和引用的關(guān)系可以說(shuō)是互相關(guān)聯(lián),卻又彼此獨(dú)立。彼此獨(dú)立主要表現(xiàn)在:引用是可以改變的,它可以指向別的對(duì)象,譬如上面的s,你可以給它另外的對(duì)象,如:
s = new StringBuffer("Java");
這樣一來(lái),s就和它指向的第一個(gè)對(duì)象脫離關(guān)系。
?
從存儲(chǔ)空間上來(lái)說(shuō),對(duì)象和引用也是獨(dú)立的,它們存儲(chǔ)在不同的地方,對(duì)象一般存儲(chǔ)在堆中,而引用存儲(chǔ)在速度更快的堆棧中。
?
引用可以指向不同的對(duì)象,對(duì)象也可以被多個(gè)引用操縱,如:
StringBuffer s1 = s;
這條語(yǔ)句使得s1和s指向同一個(gè)對(duì)象。既然兩個(gè)引用指向同一個(gè)對(duì)象,那么不管使用哪個(gè)引用操縱對(duì)象,對(duì)象的內(nèi)容都發(fā)生改變,并且只有一份,通過(guò)s1和s得到的內(nèi)容自然也一樣,(String除外,因?yàn)?span style="font-family:'Times New Roman'">String始終不變,String s1=”AAAA”; String s=s1,操作s,s1由于始終不變,所以為s另外開(kāi)辟了空間來(lái)存儲(chǔ)s,)如下面的程序:
StringBuffer s;
s = new StringBuffer("Java");
StringBuffer s1 = s;
s1.append(" World");
System.out.println("s1=" + s1.toString());//打印結(jié)果為:s1=Java World
System.out.println("s=" + s.toString());//打印結(jié)果為:s=Java World
??
上面的程序表明,s1和s打印出來(lái)的內(nèi)容是一樣的,這樣的結(jié)果看起來(lái)讓人非常疑惑,但是仔細(xì)想想,s1和s只是兩個(gè)引用,它們只是操縱桿而已,它們指向同一個(gè)對(duì)象,操縱的也是同一個(gè)對(duì)象,通過(guò)它們得到的是同一個(gè)對(duì)象的內(nèi)容。這就像汽車的剎車和油門,它們操縱的都是車速,假如汽車開(kāi)始的速度是80,然后你踩了一次油門,汽車加速了,假如車速升到了120,然后你踩一下剎車,此時(shí)車速是從120開(kāi)始下降的,假如下降到60,再踩一次油門,車速則從60開(kāi)始上升,而不是從第一次踩油門后的120開(kāi)始。也就是說(shuō)車速同時(shí)受油門和剎車影響,它們的影響是累積起來(lái)的,而不是各自獨(dú)立(除非剎車和油門不在一輛車上)。所以,在上面的程序中,不管使用s1還是s操縱對(duì)象,它們對(duì)對(duì)象的影響也是累積起來(lái)的(更多的引用同理)。
?
只有理解了對(duì)象和引用的關(guān)系,才能理解參數(shù)傳遞。
一般面試題中都會(huì)考Java傳參的問(wèn)題,并且它的標(biāo)準(zhǔn)答案是Java只有一種參數(shù)傳遞方式:那就是按值傳遞,即Java中傳遞任何東西都是傳值。如果傳入方法的是基本類型的東西,你就得到此基本類型的一份拷貝。如果是傳遞引用,就得到引用的拷貝。
?
一般來(lái)說(shuō),對(duì)于基本類型的傳遞,我們很容易理解,而對(duì)于對(duì)象,總讓人感覺(jué)是按引用傳遞,看下面的程序:
public class ObjectRef {//基本類型的參數(shù)傳遞public static void testBasicType(int m) {System.out.println("m=" + m);//m=50m = 100;System.out.println("m=" + m);//m=100}//參數(shù)為對(duì)象,不改變引用的值 ??????public static void add(StringBuffer s) {s.append("_add");}//參數(shù)為對(duì)象,改變引用的值 ?????public static void changeRef(StringBuffer s) {s = new StringBuffer("Java");}public static void main(String[] args) {int i = 50;testBasicType(i);System.out.println(i);//i=50StringBuffer sMain = new StringBuffer("init");System.out.println("sMain=" + sMain.toString());//sMain=initadd(sMain);System.out.println("sMain=" + sMain.toString());//sMain=init_addchangeRef(sMain);System.out.println("sMain=" + sMain.toString());//sMain=init_add}}以上程序的允許結(jié)果顯示出,testBasicType方法的參數(shù)是基本類型,盡管參數(shù)m的值發(fā)生改變,但并不影響i。
????? add方法的參數(shù)是一個(gè)對(duì)象,當(dāng)把sMain傳給參數(shù)s時(shí),s得到的是sMain的拷貝,所以s和sMain指向同一個(gè)對(duì)象,因此,使用s操作影響的其實(shí)就是sMain指向的對(duì)象,故調(diào)用add方法后,sMain指向的對(duì)象的內(nèi)容發(fā)生了改變。
??????在changeRef方法中,參數(shù)也是對(duì)象,當(dāng)把sMain傳給參數(shù)s時(shí),s得到的是sMain的拷貝,但與add方法不同的是,在方法體內(nèi)改變了s指向的對(duì)象(也就是s指向了別的對(duì)象,牽著氣球的繩子換氣球了),給s重新賦值后,s與sMain已經(jīng)毫無(wú)關(guān)聯(lián),它和sMain指向了不同的對(duì)象,所以不管對(duì)s做什么操作,都不會(huì)影響sMain指向的對(duì)象,故調(diào)用changeRef方法前后sMain指向的對(duì)象內(nèi)容并未發(fā)生改變。
?
對(duì)于add方法的調(diào)用結(jié)果,可能很多人會(huì)有這種感覺(jué):這不明明是按引用傳遞嗎?對(duì)于這種問(wèn)題,還是套用Bruce Eckel的話:這依賴于你如何看待引用,最終你會(huì)明白,這個(gè)爭(zhēng)論并沒(méi)那么重要。真正重要的是,你要理解,傳引用使得(調(diào)用者的)對(duì)象的修改變得不可預(yù)期。
?
public class Test { public int i,j; public void test_m(Test a){ Test b = new Test();b.i = 1;b.j = 2;a = b;}public void test_m1(Test a ){ a.i = 1;a.j = 2;}public static void main(String argv[]){ Test t= new Test();t.i = 5;t.j = 6;System.out.println( "t.i = "+ t.i + " t.j= " + t.j); //5,6t.test_m(t);System.out.println( "t.i = "+ t.i + " t.j= " + t.j); //5,6,a和t都指向了一個(gè)對(duì)象,而在test_m中s又指向了另一個(gè)對(duì)象,所以對(duì)象t不變!!!t.test_m1(t);System.out.println( "t.i = "+ t.i + " t.j= " + t.j); //1,2}}答案只有一個(gè):Java里都是按值傳遞參數(shù)。而實(shí)際上,我們要明白,當(dāng)參數(shù)是對(duì)象時(shí),傳引用會(huì)發(fā)生什么狀況(就像上面的add方法)?
?
=========================================================================
樓主,這樣來(lái)記這個(gè)問(wèn)題
如下表達(dá)式:
A a1 = new A();
它代表A是類,a1是引用,a1不是對(duì)象,new A()才是對(duì)象,a1引用指向new A()這個(gè)對(duì)象。
在JAVA里,“=”不能被看成是一個(gè)賦值語(yǔ)句,它不是在把一個(gè)對(duì)象賦給另外一個(gè)對(duì)象,它的執(zhí)行過(guò)程實(shí)質(zhì)上是將右邊對(duì)象的地址傳給了左邊的引用,使得左邊的引用指向了右邊的對(duì)象。JAVA表面上看起來(lái)沒(méi)有指針,但它的引用其實(shí)質(zhì)就是一個(gè)指針,引用里面存放的并不是對(duì)象,而是該對(duì)象的地址,使得該引用指向了對(duì)象。在JAVA里,“=”語(yǔ)句不應(yīng)該被翻譯成賦值語(yǔ)句,因?yàn)樗鶊?zhí)行的確實(shí)不是一個(gè)賦值的過(guò)程,而是一個(gè)傳地址的過(guò)程,被譯成賦值語(yǔ)句會(huì)造成很多誤解,譯得不準(zhǔn)確。
?
再如:
A a2;
它代表A是類,a2是引用,a2不是對(duì)象,a2所指向的對(duì)象為空null;
?
再如:
a2 = a1;
它代表,a2是引用,a1也是引用,a1所指向的對(duì)象的地址傳給了a2(傳址),使得a2和a1指向了同一對(duì)象。
綜上所述,可以簡(jiǎn)單的記為,在初始化時(shí),“=”語(yǔ)句左邊的是引用,右邊new出來(lái)的是對(duì)象。
在后面的左右都是引用的“=”語(yǔ)句時(shí),左右的引用同時(shí)指向了右邊引用所指向的對(duì)象。
再所謂實(shí)例,其實(shí)就是對(duì)象的同義詞。
?
?
?
如果需要賦值,就需要類實(shí)現(xiàn)Cloneable接口,實(shí)現(xiàn)clone()方法。
class D implements Cloneable{//實(shí)現(xiàn)Cloneable接口String sex;D(String sex){this.sex=sex;}@Overrideprotected Object clone() throws CloneNotSupportedException {// 實(shí)現(xiàn)clone方法return super.clone();} }賦值的時(shí)候:
D d=new D("男"); D d2=(D) d.clone();//把d賦值給d2如果類中的變量不是主類型,而是對(duì)象,也需要調(diào)用該對(duì)象的clone()方法
下面是一個(gè)完整的例子:
轉(zhuǎn)載地址:
原作1:http://blog.sina.com.cn/s/blog_4cd5d2bb0100ve9r.html
原作1:clone:http://bxkqzjt521.blog.163.com/blog/static/202818420119102362458/
再次編輯:http://www.cnblogs.com/focusChen/articles/2497768.html
補(bǔ)充內(nèi)容:java中的值傳遞和引用傳遞?http://blog.csdn.net/wyzsc/article/details/6341107
---感謝作者的無(wú)私奉獻(xiàn)
總結(jié)
以上是生活随笔為你收集整理的java 中对象引用,以及对象赋值的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: session 存放对象变量,及遇到的奇
- 下一篇: Qt与Tomcat服务器通信实例 po