1. 程式人生 > >Java基礎(五)Java中的引數傳遞機制

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方法中的ab變數作為引數值傳入swap方法,傳入swap方法的只是ab的副本,而不是ab本身,進入swap方法後,系統中產生了4個變數,這四個變數再記憶體中的儲存示意圖如下:


main方法中呼叫swap方法時,main方法還沒有結束。

因此係統分別為main方法和swap方法分類兩塊棧區,分別用於儲存main方法和swap方法的區域性變數

main方法中的ab變數作為引數值傳入swap方法,實際上再swap方法棧區中重新產生了兩個變數

ab,並將main方法棧區中的ab變數的值,分別賦給swap方法棧區的ab引數(就是swap方法的ab形參進行了初始化)。

此時,系統存在兩個a變數、兩個b變數,只是存在於不同的方法棧區中而已。

值傳遞的實質

上面的交換程式,main方法棧區中ab的值並沒有任何改變,程式改變的只是swap方法棧中的ab

當系統開始執行方法時,系統為形參執行初始化,就是把實參變數的值賦給方法的形參變數,方法裡操作的並不是實際的實參變數。

引用型別的引數傳遞

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方法裡,ab兩個屬性值被交換成功。不僅如此,main方法裡的swap方法直接結束後,ab兩個屬性的值也被交換了。這很容易造成一種錯覺:呼叫swap方法時,傳入swap方法的,就是dw物件本身,而不是它的複製品。但這種結論時錯誤的。

程式從main方法開始執行,main方法建立了一個DataSwap物件,並定義了一個dw引用變數來指向DataSwap物件。

建立一個物件時,系統記憶體中有兩個實體:堆記憶體中儲存了物件本身,棧記憶體中儲存了該物件的引用。

接著,程式通過引用操作DataSwap物件,把該物件的ab屬性分別賦值。如下圖:

接下來,main方法裡開始呼叫swap方法,main方法並未結束,系統會分別開闢出mainswap兩個棧區,分別用於存放mainswap方法的區域性變數。

呼叫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物件的ab屬性值後,它們看到main方法中的dw變數所引用DataSwap物件的ab屬性值也被交換了。

為了更好地證明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變數的ab屬性,依然可以輸出結果。可見,main方法裡的ds變數沒有收到任何影響。

形參長度可變長的方法

JDK1.5之後,Java允許定義形參長度可變的引數,從而允許為方法指定數量不確定的形參。

如果在定義方法時,再最後一個形參的型別後增加三點(...),則辨明該形參可接受多個引數值,多個引數值被當作陣列傳入。

限制:該中方式的引數,必須放在形參的末尾

總結一下:

Java中的引數傳遞,無論是基本型別,還是引用型別,他們傳遞的都是一份副本。基本型別傳遞的是值本身的副本,引用型別傳遞的是地址的副本。