Java語言中的值傳遞與引用傳遞
JAVA語言中的傳遞都是值傳遞嗎?有沒有引用傳遞呢?這是一個常常被討論的問題。開始以前首先來看下面的程式碼:
public class TestParameter {
// 初始值為0
protected int num = 0;
// 為方法引數重新賦值
public void change(int i) {
i = 5;
}
// 為方法引數重新賦值
public void change(TestParameter t) {
TestParameter tmp = new TestParameter();
tmp.num = 9;
t = tmp;
}
// 改變方法引數的值
public void add(int i) {
i += 10;
}
// 改變方法引數屬性的值
public void add(TestParameter pt) {
pt.num += 20;
}
public static void main(String[] args) {
TestParameter t = new TestParameter();
System.out.println("引數--基本型別");
System.out.println("原有的值:" + t.num);
// 為基本型別引數重新賦值
t.change(t.num);
System.out.println("賦值之後:" + t.num);
// 為引用型引數重新賦值
t.change(t);
System.out.println("運算之後:" + t.num);
System.out.println();
t = new TestParameter();
System.out.println("引數--引用型別");
System.out.println("原有的值:" + t.num);
// 改變基本型別引數的值
t.add(t.num);
System.out.println("賦引用後:" + t.num);
// 改變引用型別引數所指向物件的屬性值
t.add(t);
System.out.println("改屬性後:" + t.num);
}
}
這段程式碼執行結果如下:
引數--基本型別
原有的值:0
賦值之後:0
運算之後:0
引數--引用型別
原有的值:0
賦引用後:0
改屬性後:20
從上面這個程式碼的執行結果可以看出:
首先,對於基本型別,在方法體內對方法引數進行重新賦值,並不會改變原有變數的值。
其次,對於引用型別,在方法體內對方法引數進行重新賦予引用,並不會改變原有變數所持有的引用。
另外,方法體內對引數進行運算,不影響原有變數的值。方法體內對引數所指向物件的屬性進行運算,將改變原有變數所指向物件的屬性值。
為什麼會出現這樣的現象呢?這就要說到值傳遞和引用傳遞的概念了。這個問題向來是頗有爭議的,就連Think in Java 的作者Eckel都參與爭論。
大家知道,JAVA中變數有以下兩種:
1.基本型別變數,包括char、byte、short、int、long、float、double、boolean。
2.引用型別變數,包括類、介面、陣列(基本型別陣列和物件陣列)。
當基本型別的變數被當作引數傳遞給方法時,JAVA虛擬機器所做的工作是把這個值拷貝了一份,然後把拷貝後的值傳遞到了方法的內部。因此在上面的例子中,我們回頭來看看這個方法:
// 為方法引數重新賦值
public void change(int i) {
i = 5;
}
在這個方法被呼叫時,變數i和TestParameter型物件t的屬性num具有相同的值,卻是兩個不同變數。變數i是由JAVA虛擬機器建立的作用域在 change(int i)方法內的區域性變數,在這個方法執行完畢後,它的生命週期就結束了。在基本型別被作為引數傳遞給一方時,是值傳遞,在整個過程中沒有牽扯到引用這個概念。這也是大家所公認的。
在基本型別被作為引數傳遞給方式時,是值傳遞,在整個過程中根本沒有牽扯到引用這個概念。對於布林型變數當然也是如此,我們看看下面的例子:
public class TestBooleanParameter {
// 布林型值
boolean bool = true;
// 為布林型引數重新賦值
public void change(boolean b) {
b = false;
}
// 對布林型引數進行運算
public void calculate(boolean b) {
b = b && false;
// 為了方便對比,將運算結果輸出
System.out.println("b運算後的值:" + b);
}
public static void main(String[] args) {
TestBooleanParameter t = new TestBooleanParameter();
System.out.println("引數--布林型");
System.out.println("原有的值:" + t.bool);
// 為布林型引數重新賦值
t.change(t.bool);
System.out.println("賦值之後:" + t.bool);
// 改變布林型引數的值
t.calculate(t.bool);
System.out.println("運算之後:" + t.bool);
}
}
輸出結果如下:
引數--布林型
原有的值:true
賦值之後:true
運算後的值:false
運算之後:true