天天看點

Java值傳遞和引用傳遞

先明晰一下文中值傳遞和引用傳遞的含義(關于對引用的定義的争議請參考評論區)。

值傳遞:方法調用時,實參把它的值傳遞給對應的形參(或者說副本),方法執行中形式參數值的改變不影響實際參 數的值。 引用傳遞:也稱為傳位址。方法調用時,實參的引用(位址,而不是參數的值)被傳遞給方法中相對應的形參,在方法執行中,對形參的操作實際上就是對實參的操作,方法執行中形式參數值的改變将會影響實際參數的值。

代碼一

代碼二

真的沒有差別嗎?

以上是我最近寫代碼時遇到的,當我使用第一種寫法的時候,我發現<code>objects</code> 一直是null,略微詫異了一會,我換了第二種寫法,問題解決。

老司機可能一看就知道了,這是一個值傳遞和引用傳遞的經典問題。

那麼為什麼第一個不是引用傳遞?難道list不是引用類型嗎?一圖勝過千言萬語,先來張圖解釋一下。

Java值傳遞和引用傳遞

引用本身以及基本資料類型是存放在棧裡的,而引用類型所指向的内容存放在堆内。據此,畫出了上圖所示内容,<code>objects</code>表示引用本身(堆中的位址,後文位址均表示此意),而<code>content</code>表示引用指向的具體内容(後文也均使用該詞表示引用指向的具體内容)。

解釋:

在<code>代碼一</code>中,引用<code>objects</code>,其值為<code>null</code>,是以沒有指向任何堆記憶體;

當我們調用<code>foo</code>方法時,引用<code>objects</code>首先會在<code>foo</code> 方法中被拷貝一份副本<code>objectscopy</code> ,<code>objectscopy</code>同樣也不指向任何堆内容(在foo方法中,所有的操作都是通過<code>objects</code> 的副本來操作的);

當<code>foo</code> 方法中<code>objectscopy = new arraylist&lt;&gt;();</code> 語句被調用時,content的位址&amp;content(&amp;在c/c++中表示取位址)就會被寫到棧<code>objectscopy</code>,此時<code>objectscopy</code> 就指向了content;

可以很清楚的看到,接下來所有的操作都會改變content的内容,但是很遺憾,objects不會有任何改變,始終為null。

Java值傳遞和引用傳遞

在<code>代碼二</code>中,main函數中執行了語句<code>objects = new arraylist&lt;&gt;();</code>,于是引用<code>objects</code>指向堆中的<code>content</code>;

當我們調用<code>foo</code>方法時,引用<code>objects</code>首先會在<code>foo</code> 方法中被拷貝一份副本<code>objectscopy</code> ,因為<code>objects</code>指向堆中的<code>content</code>,于是<code>objectscopy</code>指向堆中的<code>content</code>;

接下來對<code>objectscopy</code> 的所有的操作都會改變content的内容,因為objects指向的也是content,是以就改變了objects指向的内容。這就是著名的引用傳遞。

那麼代碼一是什麼傳遞?

函數中修改一個存放在棧中的資料,而傳遞進來的參數是它本身,這是什麼傳遞?或者說函數傳了一個引用參數(位址),而現在修改的是引用本身,這是什麼傳遞?

這就是地道地道的、徹頭徹尾的goddamn值傳遞。

僅看表面上傳遞的是引用類型還是值類型是無法判斷這将是值傳遞還是引用傳遞,這要取決于你具體的操作是改變引用本身(位址)還是引用指向的内容(content)。

Java值傳遞和引用傳遞

盡管在代碼一中,我傳遞的是一個引用類型,但是我修改的是引用本身,是以它是值傳遞,它真正操作的部分如上圖所示;代碼二中我修改的是content,但我傳遞的是content的引用,是以它才是引用傳遞。

是以,修改a,傳遞a的引用,這就是引用傳遞;修改a,傳遞a,這就是值傳遞。傳遞引用類型不是引用傳遞。

好繞啊。。。c++或許更好了解一些。

修改引用指向的内容(x和y),傳遞的是位址(指針)<code>int* x</code>和<code>int* y</code>(位址可以對應了解為java中的引用),這就是引用傳遞。

修改指針,傳遞的也是指針本身,這就是值傳遞。

盡管我自認為在c/c++中就已經将這兩種傳遞了解得很透徹了,但是不經意間這錯誤還是犯得徹徹底底。為此,我總結出這樣的一句話:

如果你想修改引用指向的内容,你需要傳遞引用;如果你想要修改引用本身的值,那麼你需要傳遞引用的引用,否則那隻是穿上了引用外衣的值傳遞。

最後再補充一點c++中值傳遞和引用傳遞。

值傳遞

位址傳遞

引用傳遞

除值傳遞外,位址和引用傳遞都會改變x和y的值。