內容稍多,可直接看第4點的討論結果java
前言windows
在涉及到傳遞參數給方法時,容易出現一些參數傳遞錯誤的問題,這就涉及到了參數的傳遞問題,必須搞清楚:參數是如何傳遞到方法中的?通常來講,參數的傳遞能夠分爲兩種:值傳遞和引用傳遞。數組
所謂值傳遞,就是方法中的形參得到的是實參的值,而引用傳遞,就是說方法中的形參得到的是實參的引用(地址)。app
參數的傳遞其實相似於一個賦值操做,因此接下來,先討論值和地址的問題,再討論賦值操做,最後才進行參數傳遞的討論。iphone
1.基本概念ide
首先要搞清楚一個概念,即:變量中儲存的內容是什麼。ui
變量類型分爲基本數據類型和引用類型:spa
基本數據類型的變量直接存儲的是變量的值,如int i=10; i中存儲的爲變量的值。.net
引用類型的變量存儲的則是實際對象的地址,如Dog myDog = new Dog(); myDog中存儲的僅僅是地址(也能夠稱爲引用)。3d
2.賦值操做
須要明確賦值操做的含義。
賦值操做「=」包含兩個意思:1.放棄了原有的值或引用;2.獲得了 = 右側變量的值或引用。
基本類型:= 操做表明完整複製了變量的值。例如:int a=b; a僅獲取了b的值,二者在此以後並沒有任何關係。
引用類型:= 操做表明複製了變量的引用。 例如:Dog aDog=bDog; aDog複製了bDog的引用,兩個變量都指向同一個Dog對象。
一個簡單的例子:
public class Test { public static void main(String[] args) { int x=10; int y=x; x=20; System.out.println("x:"+x); System.out.println("y:"+y); Dog aDog=new Dog(); aDog.name="阿黃"; aDog.age=1; Dog bDog=aDog; aDog.name="旺財";//改變aDog的名字 bDog.age=2;//改變bDog的年齡 System.out.println("aDog:"+aDog.name+","+aDog.age); System.out.println("bDog:"+bDog.name+","+bDog.age); } } class Dog { String name; int age; }
x:20 y:10 aDog:旺財,2 bDog:旺財,2
由結果能夠看出,對於基本數據類型,在y=x後,y僅僅是得到了x的值,二者再也不有關係了,所以輸出結果不一樣;
對於引用類型,在bDog=aDog後,bDog獲取了aDog的地址,兩個變量都是指的同一條狗。雖然分別變化了aDog的名字和bDog的年齡,但其實變的都是同一個對象,因此輸出結果相同。
3.方法中的參數傳遞討論
方法中參數傳遞的本質即爲賦值操做。
/*第一個例子:基本類型*/ void foo(int value) { value = 100; } foo(num); // num 沒有被改變
例一分析:參數傳遞,至關於執行了value=num,value得到了num的值,這以後value與num無關,因此num沒有改變。
/*第二個例子:沒有提供改變自身方法的引用類型*/ void foo(String text) { text = "windows"; } foo(str); // str 也沒有被改變
例二分析:參數傳遞後,text與str都存儲了相同的地址,指向同一個對象;但以後text從新進行了賦值操做text= "windows",text放棄了原有的引用而指向了新的對象(見上述賦值操做的兩重意思),而str仍爲原有的引用,因此str沒有改變。
/*第三個例子:提供了改變自身方法的引用類型*/ StringBuilder sb = new StringBuilder("iphone"); void foo(StringBuilder builder) { builder.append("4"); } foo(sb); // sb 被改變了,變成了"iphone4"。
例三分析:參數傳遞後,sb與builder指向同一個對象,builder.append()僅對該對象進行了增長操做,sb和builder仍指向同一對象,因此sb變爲了"iphone4"。
參數傳遞後:
執行builder.append("4")後:
/*第四個例子:提供了改變自身方法的引用類型,可是不使用,而是使用賦值運算符。*/ StringBuilder sb = new StringBuilder("iphone"); void foo(StringBuilder builder) { builder = new StringBuilder("ipad"); } foo(sb); // sb 沒有被改變,仍是 "iphone"。
例四分析:參數傳遞後,sb與builder指向同一個對象,但以後builder從新進行了賦值操做builder = new StringBuilder("ipad"),builder指向了一個新的對象,而sb仍爲指向原來的對象,因此sb沒有改變。
參數傳遞後:
執行builder = new StringBuilder("ipad")後:
4.值傳遞與引用傳遞討論結果
在上述討論中,能夠知道,參數傳遞至關於賦值操做,
對於基本數據類型,方法中的形參獲取了實參的值,便可以理解爲值傳遞;
對於引用類型,方法中的形參獲取的是實參的引用,二者指向同一對象,便可以理解爲引用傳遞。
值傳遞中,形參的改變不影響實參;引用傳遞中,形參讓對象改變,至關於讓實參也發生了改變。
也有人認爲,Java中的參數傳遞只有值傳遞,我以爲應該是把「引用傳遞」認爲是引用的地址值的傳遞,因此統稱爲值傳遞。(知識還很淺薄,只是我的理解)
5.再來兩個例子
基本數據類型比較簡單,再也不贅述。引用類型的例子再來兩個,讀者可自行分析
public class Test { private void test1(A a) { a.age = 20; System.out.println("test1方法中的age=" + a.age); } public static void main(String[] args) { Test t = new Test(); A a = new A(); a.age = 10; t.test1(a); System.out.println("main方法中的age=" + a.age); } } class A { public int age = 0; }
test1方法中的age=20
main方法中的age=20
public class Test { private void test2(A a) { a = new A();// 新加的一行 a.age = 20; System.out.println("test2方法中的age=" + a.age); } public static void main(String[] args) { Test t = new Test(); A a = new A(); a.age = 10; t.test2(a); System.out.println("main方法中的age=" + a.age); } } class A { public int age = 0; }
test2方法中的age=20
main方法中的age=10
提示:test2中新加的一行使形參從新指向了一個新的對象,因此以後操做就和主程序中的實參無關了。
6.String類型和數組的參數傳遞
String類型和數組的參數傳遞比較容易讓人困惑,後面又看到相關的文章,這裏再討論一下
public class Test { public void arrayPassTest(String s, String[] ss) { s = "bad"; ss[0] = "bbb"; } public static void main(String[] args) { String s1 = new String("good"); String[] ss1 = { "aaa" }; // string數組,只有一個元素 Test test = new Test(); test.arrayPassTest(s1, ss1); System.out.println(s1 + ss1[0]); } }
goodbbb
對於String類型,
不少人將String類型的參數傳遞理解爲值傳遞(相似於基本數據類型),但其實String 類型的傳遞與其餘引用類型相同,仍然是引用傳遞,也便是地址傳遞。
既然是引用傳遞,那爲何s1不隨s改變呢?在方法arrayPassTest()中,s="bad"其實至關因而s=new String("bad"),s指向了一個新的String對象,與實參s1再也不是同一個對象了,因此s1不變。(明白new String(),將String類型和其餘引用類型同樣理解就能夠了。)
對於數組,
數組也是一種引用類型,ss和ss1指的是同一個數組對象,ss[0]="bbb"只是將該數組對象中的第一個元素改變了,並無新的數組對象產生。ss和ss1指向的數組對象始終都是同一個,因此ss1發生了改變。
7.參考來源
《Head First Java》
本文內容是根據參考內容 結合本身的我的理解寫的,若有錯誤,請多多指正。