Java中是引用傳遞還是值傳遞?

前言

在學習Java編程語言過程中最容易讓你產生誤解的問題之一就是 java是值傳遞還是引用傳遞。今天就來圍繞這個話題揭開迷霧。

概念

首先先來認識一下什麼是值傳遞什麼是引用傳遞。

  • 值傳遞: 將方法實際參數值複製到另一個變量,然後複製的對象被傳遞,這就是爲什麼它被稱爲“值傳遞”

  • 引用傳遞:將實際參數的引用傳遞給該方法,這就是爲什麼它被引用稱爲“傳遞”的原因。

例子分析1

問題:如果java是使用引用傳遞的話,爲什麼在函數中 變量的交換會沒有卵用呢?

答案:java通過引用來操作對象,並且所有Object類型的變量都是引用一個地址。但是,java傳遞方法參數並不是引用傳遞,而是值傳遞。

來個例子,大家感受一下:

public void badSwap(int var1, int var2)
{
  int temp = var1;
  var1 = var2;
  var2 = temp;
}

當badSwap()方法結束,原先作爲實際參數傳遞進來的變量仍然是它們原來的值,也就是這個方法然無卵用。如果把這個方法的參數由int改變成Object類,結果依然一樣。因爲java是通過值傳遞來傳遞對象引用的。這麼說可能不太清晰,再來一個例子。

public void tricky(Point arg1, Point arg2)
{
  arg1.x = 100;
  arg1.y = 100;
  Point temp = arg1;
  arg1 = arg2;
  arg2 = temp;
}
public static void main(String [] args)
{
  Point pnt1 = new Point(0,0);
  Point pnt2 = new Point(0,0);
  System.out.println("X: " + pnt1.x + " Y: " +pnt1.y); 
  System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);
  System.out.println(" ");
  tricky(pnt1,pnt2);
  System.out.println("X: " + pnt1.x + " Y:" + pnt1.y); 
  System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);  
}

構造了兩個對象pnt1和pnt2,並且初始的x,y值都是0。之後傳入tricky()方
法,在方法中修改行參args1的x,y的值,之後交換行參的指向。

執行main方法 ,輸入如下

X: 0 Y: 0
X: 0 Y: 0
X: 100 Y: 100
X: 0 Y: 0

根據輸出結果,可以發現實際參數pnt1的值被修改了,pnt2的屬性沒有變,也就是pnt1盒pnt2的交換失敗了!這裏最容易被疑惑。
pnt1和pnt2肯定是對象引用,當把pnt1和pnt2傳入到tricky()方法當中,
由於java是值傳遞,也就類似於複製,相當於複製和一個和pnt1一樣的行參變量arg1,它也擁有了一個和pnt1變量同一指向的引用。圖一展示了通過值傳遞之後兩個引用指向同一個地址。

圖1
圖1。方法中被傳入對象引用參數之後,一個對象至少會有兩個引用

上面的例子由於兩個變量的引用都指向了同一個對象,所以在方法中修改對象的值會生效。但是由於arg1是copy的一個變量,所以交換的話只是交換了arg1的指向,這就是爲什麼交換會失敗的原因。

圖2展示了僅僅是方法參數裏的引用交換了,而並不是原始的參數交換。

如果需要成功交換pnt1和pnt2的引用,只能在外部直接修改他們的引用即可。

圖2
圖2:java通過值傳遞,copy了一個和pnt1一樣的變量,他們擁有同樣的引用。在方法調用之後,僅僅是交換了arg1和arg2的引用。

在國內可能有大部分的人清楚傳遞的規律,但是他們依然習慣是基本類型是值傳遞,引用類型變量就是引用傳遞,因爲方法中的參數的確有了外部變量的引用。這個看個人理解。我更偏向於是值傳遞:通過值傳遞之後方法裏的參數擁有了和實際參數一樣的值(基礎類型爲值,對象類型爲引用),所以才擁有了引用。而如果是引用傳遞的話,那就是是直接傳遞一個存放於堆區的對象給(也就是直是複製了一個對象)。當然這只是我個人的認識。

例子分析2

public class Test {

    private static int  a;
    private int b;
    public static void main(String[] args) {
        System.out.println(a);
        modify(a);
        System.out.println(a);
        return;
    }
    private static void modify(int a) {
        a++;
    }

}

知道了java是值傳遞的,結果很清楚了

輸出a的值肯定不變。

java總是通過值傳遞而不是引用傳遞,再來一個例子

例子分析3

public class Balloon {

    private String color;

    public Balloon(){}

    public Balloon(String c){
        this.color=c;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

Balloon類擁有一個color屬性。

public class Test {

    public static void main(String[] args) {

        Balloon red = new Balloon("Red"); //memory reference 50
        Balloon blue = new Balloon("Blue"); //memory reference 100

        swap(red, blue);
        System.out.println("red color="+red.getColor());
        System.out.println("blue color="+blue.getColor());

        foo(blue);
        System.out.println("blue color="+blue.getColor());

    }

    private static void foo(Balloon balloon) { //baloon=100
        balloon.setColor("Red"); //baloon=100
        balloon = new Balloon("Green"); //baloon=200
        balloon.setColor("Blue"); //baloon = 200
    }

    //Generic swap method
    public static void swap(Object o1, Object o2){
        Object temp = o1;
        o1=o2;
        o2=temp;
    }
}

由於之前分析過一個例子,這裏我就不詳細分析具體的每一個步驟。

大家可以去看該例子的原文出處,有每一步的分析過程,而且還有視頻詳細講解(應該是要翻牆)

例子分析3
例子3分析鏈接:Java is Pass by Value and Not Pass by Reference

關於引用的知識,大家可以看我的文章 堆和棧

結語

最近太忙人也有點懶散所以很久沒更新博客了,得趕快恢復過來。有問題請留言。點贊只需一秒,動力卻是永恆,給我點個贊吧!期待你的關注!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章