歡迎您光臨本站 註冊首頁

java經典問題 傳值還是傳引用

←手機掃碼閱讀     火星人 @ 2014-03-26 , reply:0

  經典的問題,但卻不容易弄懂,尤其對有c基礎的java程序員來說,更容易引起混亂,這裡我試圖簡單點描述。

  「java函數是傳值的,java函數傳遞的參數是對象的引用」

  這兩句話好像初聽上去有些矛盾,但卻是事實,因而引起很多初學者的混亂。在這裡我試圖據個簡單的例子來說明java的這個特性,可能不全面,希望大家來補全。
QUOTE:
public class TestRef {



public static void main(String[] args)

{

ValueObject vo1 = new ValueObject("A", 1);

System.out.println("after vo1: " + vo1.getName()); //=A



changeValue1(vo1);

System.out.println("after changeValue1: " + vo1.getName());

//=A1, changed



changeValue2(vo1);

System.out.println("after changeValue2: " + vo1.getName());

//=A1, changeValue2內部的賦值不會影響這裡。

}



/**

* 使用vo1自身的函數對其內部數據進行改變是有效的,函數外可反映出來

* 這種object稱為可變的(mutable)

* @param vo1

*/

private static void changeValue1(ValueObject vo1) {

vo1.setName("A1");

}



/**

* 在函數內給vo1重新賦值不會改變函數外的原始值

* @param vo1

*/

private static void changeValue2(ValueObject vo1) {

vo1 = new ValueObject("B", 2);

System.out.println("inside changeValue2: "+ vo1.getName());

//=B,賦值操作引起的結果變化僅在changeValue2內部有效

}

}



class ValueObject {



public ValueObject() {}



public ValueObject(String name, int id)

{

this.name = name;

this.id = id;

}



private String name;

private int id;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

  解釋,vo1作為一個object,當它被用作函數參數的時候傳遞給函數的是一個引用值,這個名稱有點怪,又有引用又有值,到底是引用還是值呢,就看你怎麼理解了。如果你是去考試,官方的答案是值。可是看起來又象引用啊,希望從這個例子,你能理解java參數傳遞和和C/C++程序中的引用傳遞的不同的地方。另外,這也是java作為OO語言的特性之一:封裝的體現。

先講一下對象賦值的關係,舉例來說,下列代碼:

ValueObject v2, v3;
v2 = new ValueObject("C", 3); 粗體的部分創建了一個數據結構,假設存放在內存地址A000,賦值給句柄 v2
v3 = new ValueObject("D", 4); 粗體的部分創建了一個數據結構,假設存放在內存地址B000,賦值給句柄 v3
v2 = v3; 這句話的作用是把操作B000的地址的句柄的值付給了v2的句柄,使得v2和v3一樣操作B000的地址,

這意味著:

1.原來v2指向的地址A000變成無主的內存地址,將自動被jvm回收。
2.既然v2和v3指向同一片地址,對v3的修改v2也能得到,反之亦然。

整理得下列代碼,請感興趣的朋友運行驗證
QUOTE:
ValueObject v2 = new ValueObject("C", 3);
ValueObject v3 = new ValueObject("D", 4);
v2 = v3;
System.out.println("after v2=v3");
System.out.println("v2= "+ v2.getName());//=D
System.out.println("v3= "+ v3.getName());//=D
v3.setName("C1");
System.out.println("after v3 setnameTo C1");
System.out.println("vo2= "+ v2.getName());//=C1
System.out.println("vo3= "+ v3.getName());//=C1

  因此,可以得出結論,java中對象的每個實例(instance, 比如vo1, v2, v3 都是ValueObject的實例)的內存地址是唯一的,它一旦被創建,能夠對這個地址進行操作的就是每個實例自己,如果ValueObject類中沒有public void setName之類的方法對這個類的實例中的數據進行修改的話,程序是沒有任何別的方法可以修改ValueObject類的實例中的數據,這個就是java的封裝特性。對於不提供修改內部數據的方法的類,我們稱為不可變(immutable)的類。在函數中對傳入的參數變數進行賦值操作,只能在函數範圍內改變局部變數指向的引用地址,但是不會改變原始地址的內容。因此,在changeValue2(...)函數內部的vo1和函數外的vo1雖然名字相同,但是實際上是不同的實例變數,只不過指向了和函數外的vo1同樣的地址,所以當我們用vo1=... 對其進行賦值的時候,只不過是把函數內的臨時變數指向了新的地址,並沒有改變原始vo1內存地址中的內容。這就是在運行changeValue2(...)之後,vo1的值在main範圍內仍然沒有被修改的原因。而changeValue1裡面是調用的ValueObject本身的function來更改其內容,因此是原始內存地址中的數據被更改了,所以是全局有效的。

[火星人 ] java經典問題 傳值還是傳引用已經有542次圍觀

http://coctec.com/docs/linux/show-post-189777.html