首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 參數傳遞機制深度解析:值傳遞與引用傳遞的真相

     編輯:Java 相關知識

核心結論先行

Java 唯一支援「值傳遞」。 所謂「引用傳遞」實為「傳遞引用的值」(傳遞物件參考的副本)。理解此點是避免 Java 參數傳遞誤區的關鍵。

程式碼實證分析

class Number {
    int i;
}

public class Assignment {
    public static void main(String[] args) {
        Number n1 = new Number();
        Number n2 = new Number();
        n1.i = 9;
        n2.i = 47;
        System.out.println("1: n1.i= " + n1.i + ", n2.i= " + n2.i);
        
        n1 = n2;  // 關鍵操作:參考重新指向
        
        System.out.println("2: n1.i= " + n1.i + ", n2.i= " + n2.i);
        
        n1.i = 27;  // 透過 n1 修改物件內容
        
        System.out.println("3: n1.i= " + n1.i + ", n2.i= " + n2.i);
    }
}

執行結果

1: n1.i= 9, n2.i= 47
2: n1.i= 47, n2.i= 47
3: n1.i= 27, n2.i= 27

記憶體模型逐步拆解

初始狀態 (步驟 1 後)

Stack (main)          Heap
┌──────────────┐     ┌──────────────┐
│ n1 ──────────┼────►│ Number obj   │
│              │     │ i = 9        │
└──────────────┘     └──────────────┘
┌──────────────┐     ┌──────────────┐
│ n2 ──────────┼────►│ Number obj   │
│              │     │ i = 47       │
└──────────────┘     └──────────────┘

n1、n2 為區域變數 (儲存在 stack),各自持有指向不同 Number 物件的參考 (記憶體位址)。

執行 n1 = n2 後 (步驟 2)

Stack (main)          Heap
┌──────────────┐     ┌──────────────┐
│ n1 ──────────┼────┐│ Number obj   │
│              │    ││ i = 9        │ (無參考指向,成 GC 目標)
└──────────────┘    │└──────────────┘
┌──────────────┐    │
│ n2 ──────────┼────┘
│              │     ┌──────────────┐
└──────────────┘     │ Number obj   │
                     │ i = 47       │◄── n1、n2 共同指向同一物件
                     └──────────────┘

n1 = n2 將 n2 的參考值 (記憶體位址) **複製** 給 n1。此為「值傳遞」——傳遞的是參考的值,而非參考本身。原 n1 指向的物件失去參考,成為垃圾回收目標。

執行 n1.i = 27 後 (步驟 3)

Stack (main)          Heap
┌──────────────┐     ┌──────────────┐
│ n1 ──────────┼────┐│ Number obj   │
│              │    ││ i = 27       │◄── 透過任一參考修改,影響共享物件
└──────────────┘    │└──────────────┘
┌──────────────┐    │
│ n2 ──────────┼────┘
│              │
└──────────────┘

n1、n2 指向同一物件,透過任一參考修改欄位,另一參考讀取時皆見更新值。這是「共享可變物件」特性,非「引用傳遞」。

方法呼叫參數傳遞實證

情況 A:基本型別 (確實為值傳遞)

void modifyPrimitive(int x) {
    x = 20;  // 修改副本
}

int a = 10;
modifyPrimitive(a);
// a 仍為 10

情況 B:物件參考 (傳遞參考的值)

void modifyReference(Number num) {
    num.i = 20;        // ✅ 修改共享物件內容 - 可見
    num = new Number(); // ❌ 修改區域參考指向 - 外部不可見
    num.i = 30;
}

Number n = new Number();
n.i = 10;
modifyReference(n);
// n.i == 20 (第一行生效)
// n.i != 30 (第二行建立新物件,區域變數 num 指向新物件,外部 n 不受影響)

情況 C:嘗試交換兩物件參考 (失敗)

void swap(Number a, Number b) {
    Number temp = a;
    a = b;
    b = temp;  // 僅交換區域參考副本
}

Number n1 = new Number(); n1.i = 1;
Number n2 = new Number(); n2.i = 2;
swap(n1, n2);
// n1.i 仍為 1, n2.i 仍為 2

若需交換,必須回傳新物件、使用陣列/集合包裝、或設計可變容器類別。

關鍵概念釐清表

概念說明Java 是否支援
值傳遞 複製變數實際值 (基本型為資料值,參考型為記憶體位址) ✅ 唯一機制
引用傳遞 傳遞變數別名,可在方法內修改呼叫端變數綁定的物件 ❌ 不支援
共享可變物件 多參考指向同一物件,透過任一參考修改狀態皆可見 ✅ 參考型別天然特性
參考副本 方法參數獲得呼叫端參考的複製品,指向同一物件 ✅ 參數傳遞本質

實務防坑指南

  1. 防禦性複製:建構子/Setter 接收可變物件 (Date, 集合, 自訂類) 時,建立副本儲存
  2. 不可變物件:String、基本型別包裝類、java.time 類別天然安全,無須防禦性複製
  3. 集合傳遞:傳回內部集合時回傳 Collections.unmodifiableList() 或副本
  4. 方法設計:明確文件化「是否修改參數物件狀態」,避免副作用驚喜

總結

Java 參數傳遞永遠是「值傳遞」。參考型別傳遞的是「指向物件的記憶體位址值」。方法內可透過此位址修改物件內容 (共享可變狀態),但無法讓呼叫端參考指向新物件。牢記此鐵律,即可精準預測所有參數傳遞行為。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved