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

weixin_34146805發表於2017-03-12

參考:http://ifeve.com/stackoverflow-reference-or-value/
   http://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value

在Java的規範裡說明了在Java中一切引數都是按值傳遞的,根本就沒有引用傳遞這一說。
理解這個概念的關鍵是要明白:

Dog myDog;

這裡宣告的並不是一個Dog物件,而是一個指向Dog物件的指標。
這是什麼意思呢,就是當你執行:

Dog myDog = new Dog("Rover");
foo(myDog);

本質上是你把建立好的Dog物件的地址傳遞給foo方法。(我說的‘本質上’其實是因為Java中的指標並不是直接的地址,不過可以簡單的理解成這樣)。
假設Dog物件在記憶體中的地址是42。那我們就是把42這個值傳遞給了foo方法。

public void foo(Dog someDog) {
    someDog.setName("Max");     // AAA
    someDog = new Dog("Fifi");  // BBB
    someDog.setName("Rowlf");   // CCC
}

讓我們來看看執行的時候會發生些什麼。

  1. someDog的值設定為42。
  2. 在AAA行
    a.someDog指向一個記憶體地址為42的Dog物件。
    b.把Dog(記憶體地址為42)物件的name屬性改為Max。
  3. 在BBB行
    a.一個新的Dog物件被建立,我們假設它的記憶體地址是74。
    b.把這個74的記憶體地址值賦給someDog。
  4. 在CCC行
    a.someDog指向一個記憶體地址為74的Dog物件。
    b.把Dog(記憶體地址為74)物件的name屬性改為Rowlf。
  5. 方法執行完畢。
    現在讓我們來想想在這個方法外面發生了什麼:
    myDog改變了嗎?
    這個問題的關鍵在於:
    要明確myDog是一個指標,而不是一個實際的Dog物件。所以答案是它沒有改變,myDog的值還是42;它指向的還是最開始的那個Dog物件(雖然在foo方法中的AAA行把它指向物件的name屬性改成了Max,但是它指向的還是那個最初的Dog物件)。
    這驗證了改變所指物件的屬性,但沒有改變其指向。
    Java的執行機制跟C很像。你可以給一個指標賦值,然後把這個指標傳遞給一個方法,之後在這個方法中你可以改變這個指標指向物件的資料,但是你不能改變這個指標的指向。

在C++,Ada,Pascal以及其他支援引用傳遞的語言中你可以直接改變傳遞的引數。如果Java是引用傳遞的話,那麼在執行上面定義的foo方法的BBB行的時候someDog的指向就會被改變。

Let me explain this through an example:

public class Main{
     public static void main(String[] args){
          Foo f = new Foo("f");
          changeReference(f); // It won't change the reference!
          modifyReference(f); // It will modify the object that the reference variable "f" refers to!
     }
     public static void changeReference(Foo a){
          Foo b = new Foo("b");
          a = b;
     }
     public static void modifyReference(Foo c){
          c.setAttribute("c");
     }
}

I will explain this in steps:
1.Declaring a reference named f of type Foo and assign it to a new object of type Foo with an attribute "f".

Foo f = new Foo("f");


2.From the method side, a reference of type Foo with a name a is declared and it's initially assigned to null.

public static void changeReference(Foo a)

3.As you call the method changeReference, the reference a will be assigned to the object which is passed as an argument.

changeReference(f);

4.Declaring a reference named b of type Foo and assign it to a new object of type Foo with an attribute "b".

Foo b = new Foo("b");

5.a = b is re-assigning the reference a NOT f to the object whose its attribute is "b".

6.As you call modifyReference(Foo c) method, a reference c is created and assigned to the object with attribute "f".

7.c.setAttribute("c"); will change the attribute of the object that reference c points to it, and it's same object that reference f points to it.

相關文章