?
提綱:
1、版權(quán)聲明
2、前言
3、正文
4、結(jié)論
5、附注
6、參考資料
=======================================================
1、版權(quán)聲明:
??本文作者:little,原始出處:用"堆棧區(qū)數(shù)據(jù)復(fù)制"理解Java賦值和參數(shù)傳遞機(jī)制的心得?([link]http://java.learndiary.com/disDiaryContentAction.do?goalID=2716[/link]),郵件:mdx-xx@tom.com。如有任何反饋意見請聯(lián)系作者,作者會在本文原始出處隨時(shí)更新此文。轉(zhuǎn)載及引用請保留此版權(quán)聲明,謝謝。
2、前言:
??關(guān)于Java的賦值和參數(shù)傳遞是按值(by?value)進(jìn)行的還是按引用(by?reference)進(jìn)行的,這個(gè)問題曾經(jīng)迷惑了很多人,包括我。而且,我想,這個(gè)問題還將繼續(xù)迷惑一些人,包括那些C++的高手。
??在這里,我不準(zhǔn)備用“按值(by?value)”和“按引用(by?value)”這樣的術(shù)語來闡述這個(gè)問題。因?yàn)?,從字面的理解來看,這樣的術(shù)語在不同的人頭腦里有不同的含義。我試圖從Java數(shù)據(jù)(包括原始類型(primitive?type)和對象(ojbect))在內(nèi)存中的存儲這個(gè)角度,用一個(gè)自創(chuàng)的“術(shù)語”來闡述我對這個(gè)問題的理解。這個(gè)術(shù)語就是:“堆棧區(qū)數(shù)據(jù)復(fù)制(Stack?Data?Copy,簡稱SDC)”。詳細(xì)一點(diǎn)就是:在Java中,不管是賦值操作還是參數(shù)傳遞操作--針對原始類型(primitive?type),是對堆棧區(qū)的原始類型的值進(jìn)行復(fù)制;針對對象,是對儲存在堆棧區(qū)的,對象的引用中所儲存的對象的值的地址進(jìn)行復(fù)制。
??像上面摳字眼的句子讀起來比較費(fèi)力,我在后面將用兩個(gè)例子并結(jié)合一些示意圖來闡述我對這個(gè)問題的理解。希望各位朋友幫助糾正錯(cuò)誤。
3、正文:
1)、賦值操作:
例子源碼:(Assign.java)
-
-
public
?class?Assign{
- ??public?static?void?main(String[]?args){
- ????int?i?=?1;
- ????Object?o?=?new?Object();
- ????System.out.println("i?=?"?+?i?+?"?;?o?=?"?+?o?);?//?Step?1?(示意圖:3-1-1)
- ????int?j?=?i;???
- ????Object?p?=?o;
- ????System.out.println("i?=?"?+?i?+?"?;?j?=?"?+?j?+?"?;?o?=?"?+?o?+?"?;?p?=?"?+?p);?//Step?2?(示意圖:3-1-2)
- ????j++;
- ????p?=?new?Object();
- ????System.out.println("i?=?"?+?i?+?"?;?j?=?"?+?j?+?"?;?o?=?"?+?o?+?"?;?p?=?"?+?p);?//Step?3?(示意圖:3-1-3)
- ??}
- }
對上面例子的說明:
(1),Step?1中,整數(shù)i和對象o得到賦值。
示意圖3-1-1

?

從示意圖3-1-1中可以看出:整數(shù)i存儲在堆棧區(qū)(Stack);對象o的引用存儲在了堆棧區(qū),但是對象o的值卻存儲在了內(nèi)存堆中(Heap),對象o的引用存儲了對象o的地址。
Step?1在我的機(jī)器上的一次輸出結(jié)果:
- i?=?1?;?o?=?java.lang.Object@a90653
至于對象o的值輸出來怎么會是那個(gè)樣子,我只能告訴您:在java程序的一次運(yùn)行過程中,每個(gè)Object對象輸出這樣的值是唯一的,因此可以借此來判斷對象的引用指向的對象是否發(fā)生了改變。詳情請參考Java?API?文檔(下同,這里給出的是:J2SE?1.5.0?API?中文版):
[link]
http://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/java/io/PrintStream.html#println(java.lang.Object)[/link]
[link]
http://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/java/lang/Object.html#toString()[/link]
(2),Step?2中,把整數(shù)i賦值給了整數(shù)j,把對象o賦值給了對象p。
示意圖3-1-2

從示意圖3-1-2中可以看出:整數(shù)i的值復(fù)制給了整數(shù)j,整數(shù)j同樣存儲在堆棧區(qū);存儲在堆棧區(qū)的對象o的引用中存儲的對象o的地址C復(fù)制給了對象p的引用,對象p的引用同樣在堆棧區(qū)中。因?yàn)閷ο髉的引用得到了對象o的引用復(fù)制過來的對象o的值的存儲地址C,所以對象p的引用和對象o的引用都指向了在堆(heap)中的同一個(gè)對象,并且,這個(gè)對象的地址是地址C。
Step?2在我的機(jī)器上的一次輸出結(jié)果:
- i?=?1?;?j?=?1?;?o?=?java.lang.Object@a90653?;?p?=?java.lang.Object@a90653
(3),Step?3中,整數(shù)j的值加1,賦給了對象p新的對象值。
示意圖3-1-3

從示意圖3-1-3中可以看出:整數(shù)i的值不變,整數(shù)j的值加1變?yōu)?,整數(shù)在堆棧區(qū)中;新生成的對象的值存儲在了堆(Heap)中,地址為F。新生成對象的地址F存儲在了堆棧區(qū)p的引用中,替換了原來存儲在其中的地址C。于是,p的引用就指向了新生成的對象,這個(gè)新生成的對象的地址是地址F。而整數(shù)i和對象o的?(包括對象o的引用)沒有改變也不曾有任何改變(除了初次賦值)。
Step?3在我的機(jī)器上的一次輸出結(jié)果:
- i?=?1?;?j?=?2?;?o?=?java.lang.Object@a90653?;?p?=?java.lang.Object@de6ced
至此,通過上面的例子及其示意圖和說明,我得到一個(gè)結(jié)論:
在Java賦值操作中,針對原始類型(primitive?type),是對堆棧區(qū)的原始類型的值進(jìn)行復(fù)制;針對對象,是對儲存在堆棧區(qū)的,對象的引用中所儲存的對象的值的地址進(jìn)行復(fù)制。這就是術(shù)語:“堆棧區(qū)數(shù)據(jù)復(fù)制(Stack?Data?Copy,簡稱SDC)”在Java賦值操作中的闡述。
2)、方法中的參數(shù)傳遞操作:
例子源碼:(PassParameter.java)
- public?class?PassParameter{
- ??static?void?showMe(int?pi,?Object?po){
- ????System.out.println("pi?=?"?+?pi?+?"?;?po?=?"?+?po);?//?Step?2?(示意圖:3-2-2)
- ????pi++;
- ????po?=?new?Object();
- ????System.out.println("pi?=?"?+?pi?+?"?;?po?=?"?+?po);?//?Step?3?(示意圖:3-2-3)
- ??}
- ??public?static?void?main(String[]?args){
- ????int?i?=?1;
- ????Object?o?=?new?Object();
- ????System.out.println("i?=?"?+?i?+?"?;?o?=?"?+?o);?//?Step?1?(示意圖:3-1-1)
- ????showMe(i,?o);
- ????System.out.println("i?=?"?+?i?+?"?;?o?=?"?+?o);?//?Step?4?(示意圖:3-2-3)
- ??}
- }
對上面例子的說明:
(1),Step?1中,與上面Assign.java中的Step?1相同,略,下面重復(fù)其示意圖3-1-1。
示意圖3-1-1

?
Step?1在我的機(jī)器上的一次輸出結(jié)果:
- i?=?1?;?o?=?java.lang.Object@a90653
(2),Step?2中,與上面Assign.java中的Step?2類似,只是Assign.java中的整數(shù)j和對象p變成了這里的方法showMe()中的參數(shù):整數(shù)pi和對象po。并且,由于這里是參數(shù)傳遞,把Assign.java示意圖3-1-2中的“=”替換成PassParameter.java示意圖3-2-2中的“<--”,以此表示是參數(shù)傳遞。據(jù)我的理解,它們是一回事。
示意圖3-2-2

Step?2在我的機(jī)器上的一次輸出結(jié)果:
- pi?=?1?;?po?=?java.lang.Object@a90653
(3),Step?3和Step?4合并起來,見示意圖3-2-3同樣,與上面Assign.java中的Step?3類似。
示意圖3-2-3
?

Step?3和Step?4在我的機(jī)器上的一次輸出結(jié)果:
- pi?=?2?;?po?=?java.lang.Object@de6ced
- i?=?1?;?o?=?java.lang.Object@a90653
至此,通過上面的例子及其示意圖和說明,我得到一個(gè)結(jié)論:
在Java方法參數(shù)傳遞操作中,針對原始類型(primitive?type),是對堆棧區(qū)的原始類型的值進(jìn)行復(fù)制;針對對象,是對儲存在堆棧區(qū)的,對象的引用中所儲存的對象的地址的值進(jìn)行復(fù)制。這就是術(shù)語:“堆棧區(qū)數(shù)據(jù)復(fù)制(Stack?Data?Copy,簡稱SDC)”在Java方法參數(shù)傳遞操作中的闡述。
4,結(jié)論
綜上所述:在Java中,不管是賦值操作還是方法的參數(shù)傳遞操作--針對原始類型(primitive?type),是對堆棧區(qū)的原始類型的值進(jìn)行復(fù)制;針對對象,是對儲存在堆棧區(qū)的,對象的引用中所儲存的對象的值的地址進(jìn)行復(fù)制。
所以,據(jù)我的理解,術(shù)語:“堆棧區(qū)數(shù)據(jù)復(fù)制(Stack?Data?Copy,簡稱SDC)”能夠有助于理解在Java中進(jìn)行賦值和傳遞參數(shù)的機(jī)制,能夠有助于在一定程度上消除“傳值”、“傳引用”等語義上的多變性的負(fù)面影響,可以提出來供大家交流。
5,附注:
由于本人水平有限,上面的一切全是基于實(shí)踐進(jìn)行的帶有一些推測成分在內(nèi)的個(gè)人心得總結(jié)。我也以上面的自創(chuàng)術(shù)語去成功解釋過一些文章中的有關(guān)問題(如下面參考資料中的例程)。謹(jǐn)希望在能為部分Java初學(xué)者提供一個(gè)理解Java賦值和參數(shù)傳遞的手段的同時(shí),更能得到各位朋友的斧正,以便能夠?qū)@個(gè)問題形成更加正確和準(zhǔn)確的認(rèn)識。在我提高認(rèn)識的同時(shí),我會在本文原始出處:用"堆棧區(qū)數(shù)據(jù)復(fù)制"理解Java賦值和參數(shù)傳遞機(jī)制的心得?([link]
http://java.learndiary.com/disDiaryContentAction.do?goalID=2716[/link])中隨時(shí)更新此文。再次貼出我的郵件:mdx-xx@tom.com。謝謝。
6,參考資料:
1),Java參數(shù)傳遞方式?([link]
http://www.jiehoo.com/java-pass-parameter.htm[/link])
2),破除java神話之二:參數(shù)是傳址的?([link]
http://www.javaresearch.org/article/showarticle.jsp?column=544&thread=443[/link])
3),Java?應(yīng)用程序中的按值傳遞語義?([link]
http://www.javaresearch.org/article/showarticle.jsp?column=1&thread=706[/link])
4),我對《Java?應(yīng)用程序中的按值傳遞語義》的理解?([link]
http://www.javaresearch.org/article/showarticle.jsp?column=1&thread=3156[/link])
5),Thinking?in?Java,?3rd?Edition?in?Java?([download]
http://www.mindviewinc.com/downloads/TIJ-3rd-edition4.0.zip[/download])
??????????????????????????????????????????????????????????????全文完
??????????????????????????????????????????????????????????????2006年11月22日午????