Java中的值傳遞和"引用"傳遞
引言
學習過C語言的同學都很清楚在c中呼叫方法的引數有值傳遞和引用傳遞兩種方式。關於值傳遞和引用傳遞網上有許多的部落格寫的很好,這裡我就不解釋了。附上一篇:值傳遞和引用傳遞。但是使用過Java的同學可能知道,如果我們按照C語言的引數傳遞方式來理解Java中引數傳遞的話,有時候可能會和自己預料的答案有所出入。
Java中的引數傳遞方式
值傳遞
Java中的值傳遞指得是Java所提供的八種基本資料型別,不包括他們相應的包裝類。
public class ReferTest { public static void main(String[] args){ int data = 20; int res = basic(data); System.out.println("main:result data = " + res); System.out.println("main:data = " + data); } /** * 基本資料型別測試 */ public static int basic(int data){ data = 25; System.out.println("basic:data = " + data); return data; }
上面的程式碼是對基本資料型別傳遞的測試,在main方法中定義一個int型的數,在basic方法中對改值進行修改後返回,然後分別列印到控制檯。
basic:data = 25
main:result data = 25
main:data = 20
由列印結果我們可以看到有引數傳到basic中的data已經在basic中被改變,並且也返回了被改變的資料,但是data本身這個數並沒有改變,這說明,基本資料型別時通過值傳遞,main方法中的data和basic方法中的引數data並不是同一個。
引用傳遞
引用傳遞是指我們在傳遞引數的時候傳遞的是物件的引用而不是值,在Java中能被稱為引用的包括使用各種方法(new,反射)建立的物件,陣列等。這裡需要注意的是String是一個類而不是基本資料型別,他的例項是一個物件。
public static void main(String[] args){ int[] arrs ={10,6,4,8}; ReferTest.sort(arrs); System.out.println("main:"); for (int index:arrs) { System.out.print(index + " "); } } public static void sort(int[] arrs){ Arrays.sort(arrs); System.out.println("sort:"); for (int index:arrs) { System.out.print(index + " "); } System.out.println(); }
這裡使用陣列來測試,首先建立了一個無序陣列,將該陣列作為引數傳遞到sort()方法中,呼叫api排序。
sort:
4 6 8 10
main:
4 6 8 10
可以看到結果中不僅sort()中的陣列已經被排序,main()中的陣列也已經排序,在這個測試中,我們並沒有指定返回值。這個結果和預期引用傳遞引數的方式一樣,那麼這個測試能否說明Java和c++等方法一樣既有值傳遞又有引用傳遞呢?
繼續測試
public static void main(String[] args){
int[] arrs ={10,6,4,8};
ReferTest.sort(arrs);
System.out.println("main:");
for (int index:arrs) {
System.out.print(index + " ");
}
}
public static void sort(int[] arrs){
int[] testArr = {9,4,2,7};
arrs = testArr;
Arrays.sort(arrs);
System.out.println("sort:");
for (int index:arrs) {
System.out.print(index + " ");
}
System.out.println();
}
我在sort()中又建立了一個數組,內容和引數陣列完全不同,然後將此陣列賦值給引數陣列,如果是引用傳遞的話,那麼在main()中的陣列也將變成testArr。
sort:
2 4 7 9
main:
10 6 4 8
然後結果和我們期望的並不一樣,這裡sort()中陣列已經被賦值並且排序過了,但是main中的依然是之前的值,而且未經過排序。那麼很明顯Java中並不是使用的同c++一樣的引用排序。
解釋
在Java中,引用傳遞實際上不是傳遞的該引用本身,而是傳遞的該引用的一個副本,如果不對副本指向進行修改,那麼這個副本和引用值就是同一個地址,操作副本就相當於操作引用,這裡就和引用傳遞一致了。但是如對該副本修改了指向,那麼修改的知識副本值,而不會對引用本身造成影響。
arrs = testArr;
當程式走到這一步之前,大概是這樣的。圖只是為了方便理解,但是記憶體中實際不是這樣的。
形參和實參指向同一片地址,這裡就可以成為引用傳遞。而賦值過程完成後就成了這樣
形參的指向被改變了,指向的地址變化,這時候形參和實參已經沒有了任何關係。對形參的任何修改都不會反映到實參中。
其實Java中物件到底是值傳遞或引用傳遞沒必要一定要在理論上確定一個答案,只要弄明白它的執行原理,在使用中根據原理來合理的編寫程式碼就可以了。