無論基本類型還是引用類型,**方法參數傳遞的都是「值的副本」**。
public class ParamDemo {
static class MyClass { int i; }
public static void main(String[] args) {
int primitive = 2;
changePrimitive(primitive);
System.out.println("primitive=" + primitive); // 2,未變
MyClass obj = new MyClass();
obj.i = 1;
changeObject(obj);
System.out.println("obj.i=" + obj.i); // 3,內容被修改
System.out.println("obj hash=" + System.identityHashCode(obj)); // 同一對象
}
static void changePrimitive(int p) { p = 3; } // 修改副本
static void changeObject(MyClass p) {
p.i = 3; // 通過引用修改對象內容 ✓
p = new MyClass(); // 修改局部引用副本,外部 obj 不受影響 ✗
}
}
調用前: 調用 changeObject(obj) 時:
main棧幀 changeObject棧幀
┌─────────┐ ┌─────────┐
│ obj ──────────→ │ p ──────────→ ┌─────────┐
└─────────┘ └─────────┘ │ MyClass │
│ i = 1 │
堆內存 └─────────┘
┌─────────┐
│ MyClass │ ←── obj, p 共同指向
│ i = 1 │
└─────────┘
執行 p = new MyClass() 後:
changeObject棧幀
┌─────────┐
│ p ──────────→ ┌─────────┐
└─────────┘ │ MyClass │
│ i = 0 │
堆內存 └─────────┘
┌─────────┐ ┌─────────┐
│ MyClass │ │ MyClass │ ←── p 指向新對象
│ i = 3 │ │ i = 0 │
└─────────┘ └─────────┘
↑
main棧幀 obj 仍指向舊對象(i=3)
| 誤區 | 真相 |
|---|---|
| Java 對象是引用傳遞 | 引用本身是值傳遞(傳引用的副本) |
| 方法內 new 同類對象能影響外部 | 只能修改共享對象內容,不能換掉外部引用指向 |
| 基本類型包裝類傳遞能修改值 | Integer 等不可變,拆箱/裝箱產生新對象,同引用類型規則 |
數組是對象,引用傳值。方法內 arr[0]=99 影響外部;arr = new int[5] 不影響。
list.add("x") 影響外部;list = new ArrayList() 不影響。若需替換整個集合,返回新集合或用 List<T>[] 數組包裝、AtomicReference、回調函數。
完全隔離:參數經序列化→網絡傳輸→反序列化,得到全新對象圖。服務端修改參數對象絕不影響客戶端原對象。這是分布式系統「共享內存 vs 共享無」的本質區別。
同 JVM 內直接調用,遵循標準值傳遞規則,共享堆對象。
List、Map、StringBuilder)並明確文檔說明「輸出參數」。理解「值傳遞」是 Java 程序員的分水嶺。搞清「傳的是引用的值,不是引用本身」,90% 的參數修改困惑迎刃而解。