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

深度解析 Java 參數傳遞:值傳遞還是引用傳遞?

     編輯:Java 相關知識

前言:一個經典的困惑

「Java 到底是值傳遞還是引用傳遞?」這是許多 Java 學習者甚至資深開發者常爭論的話題。網路上眾說紛紜,有的說兩者皆有,有的堅稱只有值傳遞。本文將從 JVM 記憶體模型與賦值操作本質出發,結合程式碼實證,徹底釐清這個概念。

核心概念釐清

值傳遞

方法參數為基本資料類型(int、double、boolean 等)時,呼叫方法會將實際參數的值的副本傳給形式參數。兩者在記憶體中佔據不同位置,方法內修改形式參數不影響實際參數。

引用傳遞

方法參數為參考類型(物件、陣列)時,傳遞的是物件在堆記憶體中的記憶體位址(引用)的副本。實際參數與形式參數指向同一個物件,方法內透過形式參數修改物件內部狀態(如欄位值)會影響原物件,但重新指派形式參數指向新物件則不影響實際參數。

程式碼實證

案例一:基本類型傳遞

public class TestPrimitive {
    public static void main(String[] args) {
        int a = 10;
        changeNum(a);
        System.out.println("main a = " + a); // 輸出 10
    }

    static void changeNum(int a) {
        a = 100;
        System.out.println("changeNum a = " + a); // 輸出 100
    }
}

結果解析:main 中的 a 仍為 10。證明基本類型傳遞的是值的副本,方法內操作的是獨立變數。

案例二:參考類型傳遞——修改物件內部狀態

class Person {
    String name;
    Person(String name) { this.name = name; }
    @Override public String toString() { return name; }
}

public class TestRefModify {
    public static void main(String[] args) {
        Person p = new Person("Alice");
        System.out.println("before: " + p); // Alice
        modifyName(p);
        System.out.println("after: " + p);   // Bob
    }

    static void modifyName(Person p) {
        p.name = "Bob"; // 修改物件內部欄位
    }
}

結果解析:main 中的 p 變為 "Bob"。因為形式參數 p 複製了引用值,指向同一個堆上物件,透過引用修改欄位會影響原物件。

案例三:參考類型傳遞——重新指派參考

public class TestRefReassign {
    static Person p1 = new Person("p1");

    public static void main(String[] args) {
        Person p = new Person("P");
        System.out.println("before: " + p); // P
        changeObj(p);
        System.out.println("after: " + p);  // P (未變!)
    }

    static void changeObj(Person p) {
        p = new Person("pp"); // 形式參數指向新物件
        System.out.println("in method: " + p); // pp
        // p = p1; // 同理,指向靜態物件也不影響外部
    }
}

結果解析:main 中的 p 仍指向 "P"。方法內 p = new Person(...) 只是將區域變數(形式參數)的引用指向新物件,並未改變外部實際參數的引用值。

記憶體圖解與本質解析

賦值操作的本質

  • 基本類型:a = b 直接複製記憶體中的位元值。
  • 參考類型:refA = refB 複製的是記憶體位址,而非物件本身。兩個引用指向同一物件,但引用變數本身是獨立的棧上變數。

賦值操作前記憶體示意圖

賦值操作後記憶體示意圖

圖解說明:str = "world" 只是讓引用 str 指向新字串物件 "world",原 "hello" 物件不受影響(若無其他引用將被 GC 回收)。

方法參數傳遞 = 賦值操作

呼叫方法時,實際參數賦值給形式參數,本質就是一次賦值:

  • 基本類型:複製值 → 兩獨立變數。
  • 參考類型:複製引用(位址) → 兩引用指向同一物件,但引用變數本身獨立。

參考傳遞初始狀態

方法內重新指派後狀態

圖解說明:方法內 p = new Person("pp") 只改變區域引用 p 的指向,外部引用仍指向原物件。

總結

Java 只有值傳遞。

  • 基本類型傳遞的是資料值的副本。
  • 參考類型傳遞的是引用(記憶體位址)的副本。

方法參數在方法內部是區域變數,其作用域與生命週期僅限於方法執行期間。理解「引用的值被複製」這一點,就能避開所有關於參數傳遞的誤區。

常見面試追問

  1. 為什麼 String 是不可變的? 因為字串常量池與安全性考量,任何「修改」實際都是建立新物件,引用指向新物件,原物件不變。
  2. 如何在方法中交換兩個物件引用? 無法直接交換外部引用,可將物件包裝在陣列或容器類中,或返回新物件由呼叫端重新指派。
  3. 集合做為參數傳入方法後,在方法內 list.add() vs list = new ArrayList() 差異? 前者修改共享物件內容,外部可見;後者僅改變區域引用指向,外部不受影響。

掌握此核心機制,不僅能通過面試,更能在日常開發中避免因誤解參數傳遞而導致的難以排查的 Bug。

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