【Java】 參數的傳遞:值傳遞與引用傳遞討論

 

內容稍多,可直接看第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
test1結果

 

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結果

  

  提示: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》

  Java 究竟是值傳遞仍是引用傳遞?

  主要參考的是這兩個回答:12

  【Java基礎】Java:按值傳遞仍是按引用傳遞詳細解說

  java之傳遞String類型的參數

本文內容是根據參考內容 結合本身的我的理解寫的,若有錯誤,請多多指正。

相關文章
相關標籤/搜索