首先說兩個在程序設計語言中有關將參數傳遞給方法(函數)的專業術語。
- 按值調動(call by value):表示方法接收的是調用者提供的值。
- 按引用調用(call by reference):表示方法接收的是調用者提供的變量地址,也稱按址調用。
按……調用(call by)是一個標準的計算機科學術語,它用來描述各種程序設計語言(不只是Java)重方法參數的傳遞方式。
Java程序設計語言總是採用按值調用。也就是說,方法得到的是所有參數值的一個拷貝,特別是,方法不能修改傳遞給它的任何參數標量的內容。如:
double percent = 10;
harry.raiseSalary(percent);
不必想這個方法的具體實現,在方法調用之後,percent的值還是10。
看下面一個例子,假定一個方法試圖將一個參數值增加至3倍:
public static void tripleValue(double x) {
x = 3 * x;
}
// 調用方法
double percent = 10;
tripleValue(percent);
結果是什麼呢?percent變量的值增加至3倍了嗎?實際上,並沒有增加。調用這個方法之後,percent的值還是10。看一下具體的執行過程:
- x被初始化爲percent值的一個拷貝(10)。
- x被乘以3之後等於30。但是percent仍然是10。
- 方法結束之後,參數變量x不再使用。
然而,方法參數有兩種類型:
- 基本數據類型
- 對象引用
上面的例子我們可以看到,一個方法不可能修改一個基本數據類型的參數。但是,對象引用作爲參數就不同了,可以很容易地利用下面的方法實現將一個僱員的薪金提高兩倍的操作:
public static void tripleSalary(Employee x) {
x.raiseSalary(200);
}
// 調用
harry = new Employee(...);
trimpleSalary(harry);
具體的執行過程爲:
- x被初始化爲harry值的拷貝(注意,harry本身就是一個對象的引用),這裏是一個對象的引用。
- raiseSalary方法應用於這個對象引用。x和harry同時引用的那個Employee對象的薪金提高了200%。
- 方法結束後,參數變量x不再使用。當然,對象變量harry繼續引用的那個薪金增至3倍的僱員對象。
從上面例子可以看出,實現一個改變對象參數狀態的方法並不是一個難事。理由很簡單,方法得到的是對象引用的拷貝,對象引用及其他的拷貝同時引用同一個對象。
很多程序設計語言提供了兩種參數傳遞的方式:值調用和引用調用。有些程序員認爲Java程序設計語言對對象採用的是引用調用,實際上,這種理解是不對的。由於這種誤解具有一定的普遍性,我們用一個反例詳細的說一下這個問題。
// 交換兩個僱員對象的方法
public static void swap(Employee x, Employee y) {
Employee temp = x;
x = y;
y = temp;
}
如果Java對對象採用的是按引用調用,那麼這個方法就應該能夠實現交換數據的效果:
Employee a = new Employee("Alice", ...);
Employee b = new Employee("Bob", ...);
swap(a, b);
// 現在改成a是Bob,b是Alice了嗎?
但是,方法並沒有改變存儲在變量a和b中的對象引用。swap方法的參數x和y被初始化爲兩個對象引用的拷貝,這個方法交換的是這兩個拷貝。相當於x和y進行了交換,但是a和b沒有交換。
最終,在方法結束時參數變量x和y被丟棄了。原來的變量a和b仍然引用這個方法調用之前所引用的對象。
得出結論:對象引用是按值傳遞的。
總結一下Java中方法參數的使用情況:
- 一個方法不能修改一個基本數據類型的參數。
- 一個方法可以改變一個對象參數的狀態。
- 一個方法不能讓對象參數引用一個新的對象。
實例
對於方法參數中的對象引用是按值傳遞的結論,可能很多人都很迷茫,顛覆了對Java的認識。包括我也是,所以專門根據文章的內容,寫了一個簡單的例子:
// 方法參數 示例
public class Employee {
private String name;
public Employee(String name) {
this.name = name;
}
public static void swap(Employee x, Employee y) {
System.out.println("方法內交換前,x = " + x.getName() + ", y = " + y.getName());
Employee temp = x;
x = y;
y = temp;
// 方法內打印員工x和y的名字
System.out.println("方法內交換後,x = " + x.getName() + ", y = " + y.getName());
}
public String getName() {
return name;
}
public static void main(String[] args) {
Employee a = new Employee("Alice");
Employee b = new Employee("Bob");
System.out.println("交換前,a = " + a.getName() + ", b = " + b.getName());
swap(a, b);
System.out.println("調用方法交換後,a = " + a.getName() + ", b = " + b.getName());
}
}
結果如下:
捐贈
若你感覺讀到這篇文章對你有啓發,能引起你的思考。請不要吝嗇你的錢包,你的任何打賞或者捐贈都是對我莫大的鼓勵。