Java 中只有傳值呼叫。無論基本型別還是參考型別,方法接收的都是實參值的副本。
| 實參類型 | 棧上傳遞內容 | 方法內修改對外部影響 |
|---|---|---|
| 基本型別 | 數值的位元副本 | 無影響,修改僅作用於副本 |
| 參考型別 | 記憶體位址的副本 | 可透過位址修改物件內部狀態,但無法改變實參指向其他物件 |
class Value { int i = 10; }
public class Test {
public static void main(String[] args) {
Value v = new Value();
int i = 10;
System.out.println("a v.i = " + v.i + ", i = " + i); // 10, 10
test(v, i);
System.out.println("c v.i = " + v.i + ", i = " + i); // 20, 10
}
private static void test(Value v, int i) {
v.i = 20; // 修改物件內部欄位
i = 20; // 修改基本型別副本
System.out.println("b v.i = " + v.i + ", i = " + i); // 20, 20
}
}
a v.i = 10, i = 10
b v.i = 20, i = 20
c v.i = 20, i = 10
v.i 從 10 變為 20:方法收到參考副本,指向同一 Value 物件,修改欄位影響共享物件。i 仍為 10:基本型別傳值,方法修改的是副本,原變數不變。C/C++ 有真正的傳引用(&)或指標傳遞,可在方法內改變實參指向。Java 的參考型別傳遞的是「引用的值」(即位址),這在術語上稱為 Call by Sharing(共享呼叫),Barbara Liskov 於 1974 年為 CLU 語言提出此概念。Java 採用相同語義:
String、Integer 等不可變類別,傳遞後無法修改內部狀態,行為等同傳值,安全無副作用。List 或自訂 Bean,方法可 list.add() 或 bean.setName(),呼叫端可見變更;但 list = new ArrayList() 或 bean = new Bean() 僅改變區域變數指向,呼叫端不受影響。AtomicReference、單元素陣列 Object[1])或回傳新物件讓呼叫端重新賦值。牢記「Java 只有傳值」,即可避開 90% 的參數傳遞誤區,寫出更可預測的程式碼。