Java調用方法參數究竟是傳值還是傳址?


之前閱讀《Head First Java》的時候,記得里面有提到過,Java在調用方法,傳遞參數的時候,采用的是pass-by-copy的方法,傳遞一份內容的拷貝,即傳值。
舉一個最簡單的例子:

 1 public class Test {
 2     public static void main(String[] args) {
 3         int numberA = 1;
 4         int numberB = 2;
 5         swap(numberA, numberB);
 6         System.out.println(numberA);
 7         System.out.println(numberB);
 8     }
 9     
10     public static void swap(int a, int b) {
11         int c = a;
12         a = b;
13         b = c;
14     }
15 }

這里,swap(int a, int b)方法的目的是交換參數a, b 的值,不過這是不會實現的。

雖然在方法里面將變量a的值賦給了一個臨時變量temp,再將變量b的值賦給了a,最后將temp的值賦給了b。這個時候,b中保存的是之前a中的值,a中保存的也是b中的值,起碼在swap()方法里面,ab的值已經交換過來了。
但是請注意,Java調用參數的方法是pass-by-copy,也就是說,雖然在swap()方法里,參數ab(所謂的形參)獲取了 numberA 和 numberB 的值(所謂的實參),但是獲取值的方法是拷貝了實參的值賦給形參,並不是讓形參直接指向實參在內存中的地址(所謂的指針)。
所以,這段代碼輸出的結果是:

1
2
點擊查看

本例中用的是原始類型(Primitive Type)int,那么對於引用類型,是不是也是這樣的呢?讓我們來看下面這段代碼:

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 public class Test {
 5     public static void main(String[] args) {
 6         List<Integer> aList = new ArrayList<Integer>();
 7         aList.add(1);
 8         addToList(aList);
 9         System.out.println(aList);
10     }
11 
12     public static void addToList(List<Integer> list) {
13         list.add(2);
14     }
15 }

這段代碼里,我們首先新建了一個ArrayList aList,並向里面添加了一個數字“1”。然后我們嘗試調用 addToList(List<Integer> list) 方法來向aList里面添加數字“2”。這樣做是否會成功呢?

答案是,會成功的。輸出結果為:

[1, 2]
點擊查看

納尼?剛剛不是還說,Java不是pass-by-copy傳值的嗎?
難道不是應該這樣:list只是aList的一個復制品而已,不論在addToList()方法里面對list進行任何操作,最后都不會影響到aList()嗎?

前一陣子我一直是這么想的,還和同事為了一個類似的問題爭執了好久。他堅持說這里是傳址的,可我清清楚楚地記得《Head First Java》里告訴我們,Java是pass-by-value的。。
但是現在來看,被調用的方法確確實實影響了主調方法參數的值。所以問題究竟出在哪里呢?

對於這個問題,我認真思考了一下,外加最近學習的OCA里也有提到這個,整理一下我自己的理解。
首先,Java確確實實是傳值(pass-by-value)的,在上面的例子里,傳過去的確確實實也是一個copy,但是不要忘了,引用型(Reference Type)變量里面存放的值究竟是什么。
我們這里的引用型變量aList被聲明為 List<Integer>類型,也就是說,aList變量里面只可以接收對 List<Integer> 對象的引用
這里所說的“引用”,其實也就是地址,也就是指針。
也就是說,當我們調用 addToList(List<Integer> list) 方法的時候,傳給參數list的值,實際上是對相同對象的一個引用。用《Head First Java》里遙控器和家電的比喻來說的話,我們這里只有一台電視和一個遙控器。然后我們復制了一個一模一樣的遙控器出來,兩個遙控器擁有一模一樣的功能,比如開關,選台,調音量等。。而我們的電視只有一台,所以,用另外一個遙控器,是確確實實可以對這一台電視進行操作的。
所以到這里就很清晰了,Java仍然是傳值(pass-by-value)的語言,關鍵在於,你傳的是什么樣的一個值。

最后讓我們來看看OCA上面關於這部分知識點的一個小練習,有幾個小陷阱,自己好好分析:

 1 public class ReturningValues {
 2     public static void main(String[] args) {
 3         int number = 1;
 4         String letters = "abc";
 5         number(number);
 6         letters = letters(letters);
 7         System.out.println(number + letters);
 8     }
 9     
10     public static int number(int number) {
11         number++;
12         return number;
13     }
14     
15     public static String letters(String letters){
16         letters += "d";
17         return letters;
18     }
19 }

先自己做一下,做完之后再看答案:

1abcd
點擊查看

你做對了嗎?如果做錯了,最可能的原因是你沒有注意到第5行只是調用了那個方法,而並沒有獲取到方法的返回值。以后自己寫代碼的時候一定要注意避免犯這個錯誤!

 

PS:為了把答案折疊起來,本來已經用Markdown寫好了,硬是新開了一篇用TinyMCE編輯器改HTML,盡管完全沒有人會來看。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM