天天看點

Java中隻有按值傳遞,沒有按引用傳遞

最近在看<code>java核心技術</code>的時候,遇到以前遇到的一個問題,就是java除了值傳遞以外,到底有沒有引用傳遞。網上衆說紛纭,我看了具有代表性的10幾篇文章,結合書中以及自己的舉例,終于得出,java隻有按值傳遞,沒有按引用傳遞。或者可以說為<code>java隻有副本傳遞</code>,為什麼這麼說呢?請看我的論證。

<a></a>

java中所有的基本資料類型結果都一樣,在這裡用int做一下實驗。

可以看出結果并沒有改變a的值,因為java對基本資料類型的傳遞,首先為拷貝的值開辟一個記憶體,讓value指向拷貝的那個值,是以這是值傳遞。下面是記憶體分析圖:

Java中隻有按值傳遞,沒有按引用傳遞

<a href="http://benjaminwhx.com/images/passbyvalue/basictype.png">basictype</a>

java中所有的包裝資料類型和基本資料類型之間都會完成隐式的互相轉換,這也是為什麼list可以add基本資料類型的原因。

同樣這個例子和基本資料類型的結果是一樣的,傳遞的也是值的拷貝。記憶體分析和上圖一樣。

字元串由于其特殊性(字元串是不可變的),實際上方法是對原來的對象進行了一個拷貝,之後的操作都是在拷貝對象裡進行的。是以不影響原來對象的值。

b和b2對象都是在foostring方法裡面進行了一次拷貝,然後value的操作不影響b和b2的值。

b3也是傳遞了一個拷貝值到foostring2這個方法,然後concat是對這個拷貝值進行操作。

Java中隻有按值傳遞,沒有按引用傳遞

<a href="http://benjaminwhx.com/images/passbyvalue/string.png">string</a>

其實前面的還好去了解傳遞是值傳遞,那麼對象這塊如何去了解呢?

很多人都在這裡認為傳遞是引用傳遞,我們來進行一下分析。

Java中隻有按值傳遞,沒有按引用傳遞

<a href="http://benjaminwhx.com/images/passbyvalue/o1.png">object</a>

上圖的sb對象指向了對象stringbuffer,它有個值“iphone”,傳入方法後,buffer其實拿到的不是原來sb的引用,隻不過是一個副本而已。盡管它也指向iphone的記憶體位址。

第一個例子裡面,buffer在方法裡面重新指向了一個新的stringbuffer對象的記憶體位址,放着一個值”ipad”,是以原來sb指向的值并沒有改變。

Java中隻有按值傳遞,沒有按引用傳遞

<a href="http://benjaminwhx.com/images/passbyvalue/o2.png">object2</a>

第二個例子裡面,builder拿到了原來對象的引用拷貝,但是指向的位址不變,是以可以修改記憶體位址中的對象的屬性,并沒有新的引用。

Java中隻有按值傳遞,沒有按引用傳遞

<a href="http://benjaminwhx.com/images/passbyvalue/o3.png">object2</a>

是以可以看出來,我們并沒有拿到引用去操作記憶體位址中存在的值,隻不過是一個引用的拷貝,如果這個例子說的還不夠信服的話,下面的例子應該會讓人信服。

employee實體類:

Java中隻有按值傳遞,沒有按引用傳遞

<a href="http://benjaminwhx.com/images/passbyvalue/o4.png">o4</a>

上圖是swap1的交換的實作,這也正說明了為什麼輸出的值并沒有改變。因為原來對象的引用并沒有改變,改變的隻有這個引用的副本。

這也正說明了為什麼swap2是改變的交換的對象的值。

總結:

在java中并不像c++的那樣通過指針操作變量,雖然在java的最底層仍然使用的是指針,但是java的開發者已經幫我們封裝好了,讓我們接觸不到而已。基本資料類型的值傳遞的方式還是和c++的方式一樣,是一個值的拷貝。操作的是拷貝的值不會對原來的值造成影響。

而對象的操作則是引用的拷貝,其實也是一個值,是記憶體位址的值而已。它沒有完全把引用傳遞。是以翻轉它的時候不會對原來的對象造成影響,隻不過他們指向的都是同一個位址,會改變記憶體位址中的值罷了。

是以!java隻有按值傳遞!