咱們來看一個新手甚至寫了多年Java的朋友均可能不是十分肯定的問題:ide
在Java方法傳參時,到底是引用傳遞仍是值傳遞?
爲了說明問題, 我給出一個很是簡單的class定義:this
public class Foo { String attribute; Foo(String s) { this.attribute = s; } void setAttribute(String s) { this.attribute = s; } String getAttribute() { return this.attribute; } }
下面在闡明觀點時,可能會屢次用到該類。spa
關於Java裏值傳遞仍是引用傳遞,至少從表現形式上來看,兩種觀點都有支撐的論據。下面我來一一分析:3d
理由以下:
先看一段代碼code
public class Main { public static void modifyReference(Foo c){ c.setAttribute("c"); // line DDD } public static void main(String[] args) { Foo fooRef = new Foo("a"); // line AAA modifyReference(fooRef); // line BBB System.out.println(fooRef.getAttribute()); // 輸出 c } }
上述示例,輸出結果爲"c",而不是"a", 也就是傳入的fooRef裏的屬性被修改了,發生了side-effect。對象
咱們在line AAA
處新建立了一個Object Foo並將其引用fooRef
在line BBB
處傳給了方法modifyReference()
的參數cRef
, 該方法內部處理後,fooRef
指向的Object中的值從"a"變成了"c", 而引用fooRef
仍是那個引用, 所以,咱們是否能夠認爲,在line BBB
處發生了引用傳遞?blog
先留着疑問,咱們繼續往下看。ip
繼續看一段代碼get
public class Main { public static void changeReference(Foo aRef){ Foo bRef = new Foo("b"); aRef = bRef; // line EEE } public static void main(String[] args) { Foo fooRef = new Foo("a"); // line AAA changeReference(fooRef); // line BBB System.out.println(fooRef.getAttribute()); // 輸出 a } }
上述示例,輸出結果爲"a", 而不是"b", 即對傳入的fooRef內部的change並無影響外部的傳入前的值。it
咱們在line AAA
處新建立了一個Object Foo並將其引用fooRef
在line EEE
處傳給了方法changeReference()
的參數aRef
, 該方法內部引用aRef
在line DDD
處被從新賦值。若是是引用傳遞,那麼引用aRef
在line EEE
處已經被指向了新的Object, 輸出應該爲"b"
纔對,事實上是怎樣的呢?事實上輸出了"a"
,也就是說changeReference()
方法改變了傳入引用所指對象的值。
觀點1和觀點2的輸出結果多少會讓人有些困惑,別急,咱們繼續往下看。
爲了詳細分析這個問題,把上述兩段代碼合起來:
public class Main { public static void modifyReference(Foo cRef){ cRef.setAttribute("c"); // line DDD } public static void changeReference(Foo aRef){ Foo bRef = new Foo("b"); // line FFF aRef = bRef; // line EEE } public static void main(String[] args) { Foo fooRef = new Foo("a"); // line AAA changeReference(fooRef); // line BBB System.out.println(fooRef.getAttribute()); // 輸出 a modifyReference(fooRef); // line CCC System.out.println(fooRef.getAttribute()); // 輸出 c } }
下面來深刻內部來詳細分析一下引用和Object內部的變化。
來看下面圖示:
① Line AAA, 申明一個名叫fooRef
,類型爲Foo
的引用,並見其分配給一個新的包含屬性值爲"f"
的對象,該對象類型爲Foo
。
Foo fooRef = new Foo("a"); // line AAA
② Line DDD, 方法內部,申明瞭一個Foo
類型的名爲aRef
的引用,且aRef
被初始化爲null
。
void changeReference(Foo a);
③ Line CCC,changeReference()
方法被調用後,引用aRef
被分配給fooRef
指向的對象。
changeReference(fooRef);
④ Line FFF, 申明一個名叫bRef
,類型爲Foo
的引用,並見其分配給一個新的包含屬性值爲"b"
的對象,該對象類型爲Foo
。
Foo bRef = new Foo("b");
⑤ Line EEE, 將引用aRef
從新分配給了包含屬性"b"
的對象。此處注意,並不是將fooRef
從新分配,而是aRef
。
aRef = bRef;
⑥ Line CCC, 調用方法modifyReference(Foo cRef)
後,新建了一個引用cRef
並將之分配到包含該屬性"f"
的對象上,該對象同時被兩個引用fooRef
和cRef
指向着。
modifyReference(fooRef);
⑦ Line DDD,cRef.setAttribute("c");
將會改變cRef
引用指向的包含屬性"f"
的對象,而該對象同時被引用fooRef
指向着。
cRef.setAttribute("c");
此時引用fooRef
指向的對象內部屬性值"f"
也被從新設置爲"c"
。
Java內部方法傳參不是引用傳遞,而是引用自己的"值"的傳遞,歸根結底仍是值傳遞。將一個對象的引用fooRef
傳給方法的形參newRef
,將給該對象新增了一個引用,至關於多了一個alias
。咱們能夠經過這個原引用fooRef
,或這是方法參數裏的新引用newRef
去訪問、操做原對象,也能夠改變參數裏的引用newRef
自己的值,卻沒法改變原引用fooRef
的值。