Stackoverflow問答:Java是傳值還是傳引用?
譯者注:這是一篇在Stackoverflow上麵的一個經典問題,也是Java開發者容易混淆的一個問題,我節選了其中兩個vote最高的回複進行翻譯。
問題:我一直認為Java的參數是按引用傳遞,然而我看過一些文章裏說Java的參數並不是按引用傳遞的,比如這篇,這讓我很迷惑。Java中的參數到底是按引用傳遞還是按值傳遞?
回答1:
在Java裏參數是按值來傳遞的。比較難理解的可能是Java傳遞的是對象的引用,但這些引用是按值傳遞。
比如:
public static void main( String[] args ){ Dog aDog = new Dog("Max"); foo(aDog); if( aDog.getName().equals("Max") ){ //true System.out.println( "Java passes by value." ); }else if( aDog.getName().equals("Fifi") ){ System.out.println( "Java passes by reference." ); } } public static void foo(Dog d) { d.getName().equals("Max"); // true d = new Dog("Fifi"); d.getName().equals("Fifi"); // true }
在這個例子裏麵,執行完foo()方法之後,在main方法裏再調用aDog.getName()方法依然會返回”Max”,在main方法中的
aDog並沒有因為foo()的執行而被重寫,這說明了參數是按值來進行傳遞的。如果是按照引用來傳遞的話在執行完foo()
方法之後aDog.getName()將會返回”Fifi”。
就像這樣:
Dog aDog = new Dog("Max"); foo(aDog); aDog.getName().equals("Fifi"); // true public void foo(Dog d) { d.getName().equals("Max"); // true d.setName("Fifi"); }
回答2:
我剛剛發現你引用了我的文章 (譯者注:這位是提問者引用文章的作者)
在Java的規範裏說明了在Java中一切參數都是按值傳遞的,根本就沒有引用傳遞這一說。
理解這個概念的關鍵是要明白
Dog myDog;
這裏聲明的並不是一個Dog對象,而是一個指向Dog對象的指針。
這是什麼意思呢,就是當你執行
Dog myDog = new Dog("Rover"); foo(myDog);
本質上是你把創建好的Dog對象的地址傳遞給foo方法。(我說的‘本質上’其實是因為Java中的指針並不是直接的地址,不過可以簡單的理解成這樣)。
假設Dog對象在內存中的地址是42。那我們就是把42這個值傳遞給了foo方法。
如果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的指向就會被改變。
可以把引用參數當成被傳遞參數的別名,當這個別名被賦值的時候就相當於被傳遞的參數被賦值。
這對你有幫助嗎?(我會把這個回答補充到我的文章裏麵去)。
最後更新:2017-05-22 20:03:31