文章目錄
- 引入
- 參數傳遞的兩種方式
- 深入了解按值調用
- 引用資料類型的按值調用
- 按引用調用
- 證明:在java中總是值傳遞
引入
在學習程式設計語言的過程中,我相信大多數人都遇到或者經曆過一個問題,有的時候你把變量傳入了一個方法,經過方法内部的一頓操作之後,發現那個變量并沒有發生變化。如果是這樣也就算了,關鍵是有時候你傳入的變量經過方法中的操作後它又發生了變化。這是啥情況?😂莫非每次向方法中傳入參數都是一場豪賭嗎?還是遇事不決量子力學,莫非是那股神奇的力量?😎好吧扯遠了,回歸正題,歸根結底是因為你沒有徹底的弄明白程式設計語言的參數傳遞過程!
參數傳遞的兩種方式
我相信大多數人都知道參數的兩種傳遞方式:
-
(call by value)按值調用
-
(call by reference)按引用調用
這個針對大多數的程式設計語言是成立的,當然也有例外,例如:c++還有一個按指針傳遞(不過都可以按如上這兩種了解)。
在很久很久之前還有一個按名調用(call by name)
不過大家放心,現在已經棄用(( •̀ ω •́ )✧ 虛晃一槍)
按值調用
表示方法接收的是調用者提供的值。
按引用調用
表示方法接收的是調用者提供的變量位址。
講到這裡有人可能會說這些我都知道,确實很多人都知道,但也隻知道這些了,沒有深入的了解,如此的話相當于還是沒懂。
接下來上菜!🤞
深入了解按值調用
在這裡我們以java語言為例
Java程式設計語言總是采用按值調用。也就是說,方法得到的是所有參數值的一個副本。 ------《Java核心技術 卷Ⅰ》
這句話說的相當的透徹,但是俺還是要提幾句。什麼是參數值?參數值就是你那個變量裡面到底裝的是個啥玩楞。怎麼樣夠通俗吧😎。那麼什麼是副本?副本這個說法太高大上了,我們換個說法分身,這個知道吧,就是孫悟空用毫毛變的那個分身,分身終歸是分身,它并不是本體!
我們再合起來了解這句話,也就是說按值調用,就是把變量裡面裝的那個東西傳到方法的形參,自己真身還在外面,自己的分身進到方法裡去了。
現在我們來舉個例子(為了各種語言使用者都能了解,以下使用我的自創語言,跳出三界之外,不在五行之中(●ˇ∀ˇ●)):
方法A(形參){把傳進來的數加個2} //定義方法A
a = 2
方法A(a) //把a傳進方法A
a這個變量裡面裝的就是2,他把自己的2複制(分身),傳到了方法中(可以說給了形參),這個方法是對他這個分身做出一系列操作,随着方法的執行結束,他的分身也就被五馬分屍直接拜拜了,跟他的本體沒有啥關系。
來張圖解析一下:

我們都知道對于基本資料類型,變量裡面裝的是一個具體的結果;而對于引用資料類型,變量裡面裝的是位址。有些人錯誤的認為隻要是傳了個引用資料進方法,那就是按引用調用的參數傳遞方式。大錯特錯🤦♂️,接下來我們就看一下對于引用資料類型的值傳遞。
引用資料類型的按值調用
還是用我自創的語言,為大家舉個例子:
方法A(形參x){對傳入的對象的屬性進行改動}
a = 某類的對象
方法A(a)
還記得我們的方法論嗎?
按值調用,就是把變量裡面裝的那個東西傳到方法的形參,自己真身還在外面,自己的分身進到方法裡去了。
同樣的我們也按照上面的方法進行了解。a變量的裡面裝的是對象的位址,然後a把這個位址複制一份給了形參x,這個分身進入方法中完成屬性的改動。
如此一套猛如虎的操作進行下來,一看最後結果傻了眼。不是說進去的是分身,怎麼a的屬性變了?因為當a把自己的位址給了形參x之後,形參x也擁有了一個指向對象的指針,有了這條指針,當然可以進行改動!
看圖!(〃 ̄︶ ̄)人( ̄︶ ̄〃):
縱使形參x在方法執行完畢之後會被回收,但是a指向的對象還在,他造成的影響也還在。
按引用調用
按引用調用,很多人第一反應,就是傳位址,這沒錯,但是關鍵點在于傳什麼的位址。很多人不明白按引用調用的本質,就是因為這個地方沒搞明白。
給你舉個例子:
現在有一個變量a,裡面裝着某個對象的位址
将變量a傳入方法,倘若是按引用調用,傳位址,傳哪個?這就是引用調用的核心!
按引用調用
表示方法接收的是調用者提供的變量位址。
也就是說給形參的是那個0x1122的位址,而這個0x1122就是變量a在記憶體中的位址值,換句話說按引用調用相當于直接把真身放到方法中去,這回是孫悟空親自出山,而不是他的分身了!(~ ̄(OO) ̄)ブ
還沒有完全搞懂?沒事👌,接下來我們通過一個證明來加深了解!
證明:在java中總是值傳遞
此實驗參考《Java程式設計思想》
首先我們編寫一個交換兩個Employee對象的方法:
public static void swap(Employee x,Employee y){
Employee temp = x;
x = y;
y = temp;
}
如果Java對對象采用的是按引用調用,那麼這個方法就應該實作交換:
var a = new Employee{"Alice",·····};
var b = new Employee{"Bob",······}
swap(a,b);
但是最終失敗了,兩者沒有發生交換。
我們可以看看兩種情況的記憶體解析圖:
如果是按值調用:
可以看到任x、y如何亂搞,a、b都紋絲不動,是以最後的結果出來,兩者是沒有交換的。
如果是按引用調用: