切換語言為:簡體
從底層記憶體層面分析 Java 引用傳遞與值傳遞的不同

從底層記憶體層面分析 Java 引用傳遞與值傳遞的不同

  • 爱糖宝
  • 2024-08-12
  • 2049
  • 0
  • 0

前言

在Java程式設計中,引數傳遞的方式主要分為值傳遞和引用傳遞。這兩種方式決定了方法內部對引數的修改如何影響原始變數。本文將詳細探討這兩種傳遞方式的概念、應用及注意事項,並結合面試題,分析棧堆記憶體的情況。

一、值傳遞與引用傳遞的區別

  1. 值傳遞

    • 在方法呼叫時,將實際引數(原始變數)的副本傳遞給形式引數。

    • 方法內部對形式引數的修改不會影響實際引數。

    • 值傳遞適用於基本資料型別(如int、float、double等)和引用資料型別的包裝類(如Integer、Float、Double等)。

  2. 引用傳遞

    • 在方法呼叫時,將實際引數的引用(記憶體地址)傳遞給形式引數。

    • 方法內部對形式引數的修改可能會影響實際引數。

    • 引用傳遞通常發生在物件型別上。

二、面試案例分析

我們來看一個面試題,程式碼如下,看起來就幾行程式碼很簡單,那麼到時a和b最終結果是什麼呢?

從底層記憶體層面分析 Java 引用傳遞與值傳遞的不同

執行結果:輸出的是AB B,而不是AB AB,是不是覺得有點驚訝,下面開始分析一下具體原因。

從底層記憶體層面分析 Java 引用傳遞與值傳遞的不同

棧堆記憶體分析

  1. 初始狀態

    • 在棧記憶體中,有兩個變數ab,它們分別指向堆記憶體中的兩個物件AB

    • 具體來說,a的記憶體地址是000x1,它指向的物件是Ab的記憶體地址是000x2,它指向的物件是B

  2. 方法呼叫

    • 當呼叫operator(a, b)時,棧記憶體中新增了兩個區域性變數xy

    • 這兩個區域性變數也分別指向堆記憶體中的物件AB,即x的記憶體地址是000x1y的記憶體地址是000x2

  3. 操作過程

    • operator方法內部,執行x.append(y)操作。此時,x指向的物件從A變成了AB,記憶體地址仍然是000x1

    • 接著執行y = x操作。此時,y不再指向原來的物件B,而是指向x所指向的物件AB,即y的記憶體地址也變成了000x1

  4. 最終狀態

    • operator方法執行完畢後,棧記憶體中的變數ab的指向沒有發生變化。

    • a仍然指向堆記憶體中的物件AB,記憶體地址是000x1b仍然指向堆記憶體中的物件B,記憶體地址是000x2

    • 因此,輸出結果是AB B

如圖最終棧堆記憶體圖: 從底層記憶體層面分析 Java 引用傳遞與值傳遞的不同

如果改成下面程式碼,就可以看到四個變數最終的值了,跟我們上面分析的結果一樣。

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);     } }

這樣就更加清晰看出,上述透過棧堆記憶體圖分析是正確,執行結果如圖所示:

從底層記憶體層面分析 Java 引用傳遞與值傳遞的不同

三、總結

這個問題涉及到了變數的作用範圍和方法引數傳遞機制:

  1. 變數作用範圍

    • xy只在operator方法內部有效,不會影響外部變數ab

  2. 方法引數傳遞機制

    • 形參是基本資料型別時,傳遞的是資料值。

    • 形參是引用資料型別時,傳遞的是地址值。

    • 特殊型別如String、陣列、包裝類等物件是不可變的。

透過這個案例,可以清楚地看到值傳遞和引用傳遞的區別及其應用。希望本文能幫助讀者更好地理解Java中的引數傳遞機制。

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.