前言
在Java程式設計中,引數傳遞的方式主要分為值傳遞和引用傳遞。這兩種方式決定了方法內部對引數的修改如何影響原始變數。本文將詳細探討這兩種傳遞方式的概念、應用及注意事項,並結合面試題,分析棧堆記憶體的情況。
一、值傳遞與引用傳遞的區別
值傳遞:
在方法呼叫時,將實際引數(原始變數)的副本傳遞給形式引數。
方法內部對形式引數的修改不會影響實際引數。
值傳遞適用於基本資料型別(如int、float、double等)和引用資料型別的包裝類(如Integer、Float、Double等)。
引用傳遞:
在方法呼叫時,將實際引數的引用(記憶體地址)傳遞給形式引數。
方法內部對形式引數的修改可能會影響實際引數。
引用傳遞通常發生在物件型別上。
二、面試案例分析
我們來看一個面試題,程式碼如下,看起來就幾行程式碼很簡單,那麼到時a和b最終結果是什麼呢?
執行結果:輸出的是AB B
,而不是AB AB
,是不是覺得有點驚訝,下面開始分析一下具體原因。
棧堆記憶體分析
初始狀態:
在棧記憶體中,有兩個變數
a
和b
,它們分別指向堆記憶體中的兩個物件A
和B
。具體來說,
a
的記憶體地址是000x1
,它指向的物件是A
;b
的記憶體地址是000x2
,它指向的物件是B
。方法呼叫:
當呼叫
operator(a, b)
時,棧記憶體中新增了兩個區域性變數x
和y
。這兩個區域性變數也分別指向堆記憶體中的物件
A
和B
,即x
的記憶體地址是000x1
,y
的記憶體地址是000x2
。操作過程:
在
operator
方法內部,執行x.append(y)
操作。此時,x
指向的物件從A
變成了AB
,記憶體地址仍然是000x1
。接著執行
y = x
操作。此時,y
不再指向原來的物件B
,而是指向x
所指向的物件AB
,即y
的記憶體地址也變成了000x1
。最終狀態:
在
operator
方法執行完畢後,棧記憶體中的變數a
和b
的指向沒有發生變化。a
仍然指向堆記憶體中的物件AB
,記憶體地址是000x1
;b
仍然指向堆記憶體中的物件B
,記憶體地址是000x2
。因此,輸出結果是
AB B
。
如圖最終棧堆記憶體圖:
如果改成下面程式碼,就可以看到四個變數最終的值了,跟我們上面分析的結果一樣。
java程式碼解讀複製程式碼public class Test { public static void main(String[] args) { StringBuffer a = new StringBuffer("A"); StringBuffer b = new StringBuffer("B"); operator(a, b); System.out.println(a + " " + b); } public static void operator(StringBuffer x, StringBuffer y) { x.append(y); y = x; System.out.println("y:" + y + " ,x:" + x); } }
這樣就更加清晰看出,上述透過棧堆記憶體圖分析是正確,執行結果如圖所示:
三、總結
這個問題涉及到了變數的作用範圍和方法引數傳遞機制:
變數作用範圍:
x
和y
只在operator
方法內部有效,不會影響外部變數a
和b
。方法引數傳遞機制:
形參是基本資料型別時,傳遞的是資料值。
形參是引用資料型別時,傳遞的是地址值。
特殊型別如
String
、陣列、包裝類等物件是不可變的。
透過這個案例,可以清楚地看到值傳遞和引用傳遞的區別及其應用。希望本文能幫助讀者更好地理解Java中的引數傳遞機制。