首先回顧一下在程序設計語言中有關將參數傳遞給方法(或函數)的一些專業術語。值調用(call by value)表示方法接收的是調用者提供的值。而引用調用(call by reference)表示方法接收的是調用者提供的變量地址。一個方法能夠修改傳遞引用所對應的變量值,而不能修改傳遞值調用所對應的變量值。「...調用」(call by)是一個標註的計算機科學術語,它用來描述各類程序設計語言中方法參數的傳遞方式(事實上,之前還有稱(call by name),Algol程序設計語言是最古老的高級程序設計語言之一,它使用的就是這種參數傳遞方式。不過,對於今天,這種傳遞方式已經成爲歷史)。程序員
Java程序設計語言老是採用值調用。也就是說,方法獲得的是全部參數值的一個拷貝,特別是,方法不能修改傳遞給它的任何參數變量的內容。ide
例如,考慮下面的調用:函數
- double percent = 10;
- harry.raiseSalary(percent);
沒必要理睬這個方法的具體實現,在方法調用以後,percent的值仍是10.spa
下面再仔細地研究一下這種狀況。假定一個方法試圖將一個參數值增長至3倍:設計
- public static void tripleValue(double x) //doesn't work
- {
- x = 3 * x;
- }
而後調用這個方法:對象
- double percent = 10;
- tripleValue(percent);
能夠看到,不管怎樣,調用這個方法以後,percent的值仍是10。下面看一下具體的執行過程:ip
1)x被初始化爲percent值的一個拷貝(也就是10)。get
2)x被乘以3後等於30。可是percent仍然是10。string
3)這個方法結束以後,參數變量x再也不使用。it
然而,方法參數共有兩種類型:
讀者已經看到,一個方法不可能修改一個基本數據類型的參數。而對象引用做爲參數就不一樣了,能夠很容易地利用下面這個方法實現將一個僱員的薪金提升兩倍的操做:
- public static void tripleSalary(Employee x) // works
- {
- x.raiseSalary(200);
- }
當調用
- harry = new Employee(...);
- tripleSalary(harry);
時,具體的執行過程爲:
1)x被初始化爲harry值的拷貝,這裏是一個對象的引用。
2)raiseSalary方法應用於這個對象引用。x和harry同時引用的那個Employee對象的薪金提升了200%。
3)方法結束後,參數變量x再也不使用。固然,對象變量harry繼續引用那個薪金增至3倍的僱員對象。
讀者已經看到,實現一個改變對象參數狀態的方法並非一件難事。理由很簡單,方法獲得的是對象引用的拷貝,對象引用及其餘的拷貝同時引用同一個對象。
不少程序設計語言(特別是,C++和Pascal)提供了兩種參數傳遞的方式:值調用和引用調用。有些程序員(甚至本書的做者)認爲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);
- // dose a now refer to Bob, b to Alice?
可是,方法並無改變存儲在變量a和b中的對象引用。swap方法的參數x和y被初始化爲兩個對象引用的拷貝,這個方法交換的是這個兩個拷貝。
- // x refer to Alice, y to Bob
- Employee temp = x;
- x = y;
- y = temp;
- // now x refer Bob, y to Alice
最終,白費力氣。在方法結束時參數變量x和y被丟棄了。原來的變量a和b仍然引用這個方法調用以前所引用的對象。
這個過程說明:Java程序設計語言對對象採用的不是引用調用,實際上,對象引用進行的是值傳遞。
下面總結一下在Java程序設計語言中,方法參數的使用狀況:
例4-4中的程序給出了相應的演示。在這個程序中,首先試圖將一個值參數的值提升兩倍,可是沒有成功:
- Testing tripleValue:
- Before: percent = 10.0
- End of method: x = 30.0
- After: percent = 10.0
隨後,成功地將一個僱員的薪金提升了兩倍:
- Testing tripleSalary:
- Before: salary = 50000.0
- End of method: salary = 150000.0
- After: salary = 150000.0
方法結束以後,harry引用的對象狀態發生了改變。這是由於這個方法能夠經過對象引用的拷貝修改所引用對象的狀態。
最後,程序演示了swap方法的失敗效果:
- Testing swap:
- Before: a = Alice
- Before: b = Bob
- End of method: x = Bob
- End of method: y = Alice
- After: a = Alice
- After: b = Bob
能夠看出,參數變量x和y被交換了,可是變量a和b沒有收到影響。
C++註釋:C++有值調用和引用調用。引用參數標有&符號。例如,能夠輕鬆地實現void tripleValue(double& x)方法或void swap(Employee& x, Employee& y)方法實現修改它們的引用參數的目的。
- /**
- * This program demonstrates parameter passing in Java
- * @version 1.01 2012-11-13
- * @author Wucg
- */
- public class ParamTest
- {
- public static void main(String[] args)
- {
- /*
- * Test1: Methods can't modify numeric parameters
- */
- System.out.println("Testing tripleValue:");
- double percent = 10;
- System.out.println("Before: percent = " + percent);
- tripleValue(percent);
- System.out.println("After: percent = " + percent);
- /*
- * Test2: Methods can change the state of object parameters
- */
- System.out.println("\nTesting tripleSalary:");
- Employee harry = new Employee("Harry", 70000);
- System.out.println("Before: salary = " + harry.getSalary());
- tripleSalary(harry);
- System.out.println("After: salary = " + harry.getSalary());
- /*
- * Test3: Methods can't attach new objects to object parameters
- */
- System.out.println("\nTesting swap:");
- Employee a = new Employee("Alice", 70000);
- Employee b = new Employee("Bob", 60000);
- System.out.println("Before: a = " + a.getName());
- System.out.println("Before: b = " + b.getName());
- swap(a, b);
- System.out.println("After: a = " + a.getName());
- System.out.println("After: b = " + b.getName());
- }
- public static void tripleValue(double x) // doesn't work
- {
- x = 3 * x;
- System.out.println("End of method: x = " + x);
- }
- public static void tripleSalary(Employee x) // works
- {
- x.raiseSalary(200);
- System.out.println("End of method: salary = " + x.getSalary());
- }
- public static void swap(Employee x, Employee y)
- {
- Employee temp = x;
- x = y;
- y = temp;
- System.out.println("End of method: x = " + x.getName());
- System.out.println("End of method: y = " + y.getName());
- }
- }
- class Employee // simplified Employee class
- {
- public Employee(String n, double s)
- {
- name = n;
- salary = s;
- }
- public String getName()
- {
- return name;
- }
- public double getSalary()
- {
- return salary;
- }
- public void raiseSalary(double byPercent)
- {
- double raise = salary * byPercent / 100;
- salary = salary + raise;
- }
- private String name;
- private double salary;
- }