1. 程式人生 > >Java中是引用傳遞還是值傳遞?

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

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

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

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

結語

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