之前閱讀《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()方法里面,a和b的值已經交換過來了。
但是請注意,Java調用參數的方法是pass-by-copy,也就是說,雖然在swap()方法里,參數a和b(所謂的形參)獲取了 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,盡管完全沒有人會來看。。