Java基礎(五)Java中的引數傳遞機制
通過前一篇文章的介紹,我們從整體上明白了,Java類中變數的差異性、不同變數在記憶體中的儲存位置,以及變數的生命週期等。今天,我們來看一下Java中引數傳遞的機制。
形參:方法宣告時包含的引數宣告
實參:呼叫方法時,實際傳給形參的引數值
Java方法的引數傳遞機制:
Java方法的引數傳遞只有一種:值傳遞。所謂值傳遞,就是將實際引數值的副本,傳入方法內,而引數本身不會收到任何影響。
PS:傳入方法的時實際引數值的複製品,不管方法中對這個複製品如何操作,實際引數本身不會受到任何影響。
基本型別的引數傳遞
首先,來看一個demo,
public class TestPrimitiveTransfer { public static void swap(int a,int b){ int temp =a ; a = b; b = temp; System.out.println("swap方法內,a="+a+";b="+b); } public static void main(String[] args) { int a = 6; int b =9; TestPrimitiveTransfer.swap(a, b); System.out.println("main函式裡面,a="+a+";b="+b); } }
執行結果是:
swap方法內,a=9;b=6
main函式裡面,a=6;b=9
當程式執行swap方法時,系統進入swap方法,病將main方法中的a、b變數作為引數值傳入swap方法,傳入swap方法的只是a、b的副本,而不是a、b本身,進入swap方法後,系統中產生了4個變數,這四個變數再記憶體中的儲存示意圖如下:
在main方法中呼叫swap方法時,main方法還沒有結束。
因此係統分別為main方法和swap方法分類兩塊棧區,分別用於儲存main方法和swap方法的區域性變數。
main方法中的a、b變數作為引數值傳入swap方法,實際上再swap方法棧區中重新產生了兩個變數
此時,系統存在兩個a變數、兩個b變數,只是存在於不同的方法棧區中而已。
值傳遞的實質:
上面的交換程式,main方法棧區中a、b的值並沒有任何改變,程式改變的只是swap方法棧中的a、b。
當系統開始執行方法時,系統為形參執行初始化,就是把實參變數的值賦給方法的形參變數,方法裡操作的並不是實際的實參變數。
引用型別的引數傳遞
Java對於引用型別的引數傳遞,一樣採用的是值傳遞方式。但是使用物件引用時,容易發生誤解,因為它能夠交換成功。
看如下例子:
public class DataSwap { public int a; public int b; }
測試:
public class TestReferenceTransfer {
public static void swap(DataSwap ds){
int temp = ds.a;
ds.a=ds.b;
ds.b=temp;
System.out.println("swap方法內,a="+ds.a+";b="+ds.b);
// ds = null ;// 把ds直接賦為null,讓它不在指向任何有效地址。
}
public static void main(String[] args) {
DataSwap ds = new DataSwap();
ds.a=6;
ds.b=9;
swap(ds);
System.out.println("main方法內,a="+ds.a+";b="+ds.b);
}
}
執行結果:
swap方法內,a=9;b=6
main方法內,a=9;b=6
上面執行結果看:再swap方法裡,a、b兩個屬性值被交換成功。不僅如此,main方法裡的swap方法直接結束後,a、b兩個屬性的值也被交換了。這很容易造成一種錯覺:呼叫swap方法時,傳入swap方法的,就是dw物件本身,而不是它的複製品。但這種結論時錯誤的。
程式從main方法開始執行,main方法建立了一個DataSwap物件,並定義了一個dw引用變數來指向DataSwap物件。
建立一個物件時,系統記憶體中有兩個實體:堆記憶體中儲存了物件本身,棧記憶體中儲存了該物件的引用。
接著,程式通過引用操作DataSwap物件,把該物件的a、b屬性分別賦值。如下圖:
接下來,main方法裡開始呼叫swap方法,main方法並未結束,系統會分別開闢出main和swap兩個棧區,分別用於存放main和swap方法的區域性變數。
呼叫swap方法時,ds變數作為實參,傳入swap方法,同樣採用值傳遞方式:
把main方法裡ds變數的值,賦給swap方法裡的dw形參,從而完成了swap方法的ds形參的初始化。
值得指出的是:main方法裡的ds是一個引用,它儲存的DataSwap物件的地址值,檔把dw的值賦給swap方法的dw形參後,即讓swap方法的dw形參也儲存這個地址值,即也引用到堆記憶體的DataSwap物件。
如下圖,顯示了ds傳入swap方法後的儲存示意圖:
如上圖,這種引數傳遞方式,是不折不扣的值傳遞方式,系統一樣複製了ds的副本傳入swap方法,但關鍵在於ds只是一個引用變數,所以系統複製了ds變數,但並未賦值DataSwap物件。
真相揭祕:
當程式再swap方法中操作ds形參時,由於ds只是一個引用變數,故實際操作的還是堆記憶體中的DataSwap物件。此時,不管是操作main方法裡的dw變數,還是操作swap方法裡的dw引數,其實都是操作的它所引用的DataSwap物件,它們操作的是同一個物件。因此,當swap方法中交換dw引數所引用DataSwap物件的a、b屬性值後,它們看到main方法中的dw變數所引用DataSwap物件的a、b屬性值也被交換了。
為了更好地證明main方法中的dw變數和swap方法中的dw是兩個變數,我們再swap方法的最後一行增加如下程式碼:
dw = null ;//把dw直接賦為null,讓它不在指向任何有效地址。
public class TestReferenceTransfer {
public static void swap(DataSwap ds){
int temp = ds.a;
ds.a=ds.b;
ds.b=temp;
System.out.println("swap方法內,a="+ds.a+";b="+ds.b);
ds = null ;// 把ds直接賦為null,讓它不在指向任何有效地址。
}
public static void main(String[] args) {
DataSwap ds = new DataSwap();
ds.a=6;
ds.b=9;
swap(ds);
System.out.println("main方法內,a="+ds.a+";b="+ds.b);
}
}
執行結果:
swap方法內,a=9;b=6
main方法內,a=9;b=6
示意圖:
解析:
swap裡的ds變數不在指向任何有效記憶體地址,程式其他地方不做任何修改。main方法呼叫了swap方法後,再次訪問ds變數的a、b屬性,依然可以輸出結果。可見,main方法裡的ds變數沒有收到任何影響。
形參長度可變長的方法
從JDK1.5之後,Java允許定義形參長度可變的引數,從而允許為方法指定數量不確定的形參。
如果在定義方法時,再最後一個形參的型別後增加三點(...),則辨明該形參可接受多個引數值,多個引數值被當作陣列傳入。
限制:該中方式的引數,必須放在形參的末尾
總結一下:
Java中的引數傳遞,無論是基本型別,還是引用型別,他們傳遞的都是一份副本。基本型別傳遞的是值本身的副本,引用型別傳遞的是地址的副本。