參考文獻:http://www.360doc.com/content/07/1122/09/9426_838237.shtml
java程序只有傳值,沒有傳引用,傳地址的說法。但是傳遞的值可以是具體的數值,也可以是一個對象的引用。可以用這樣一句話來描述“java函數是傳值的,java函數傳遞的參數是對象的引用”。
我看到過幾個解釋這個問題的例子,不過個人感覺看過例子之后還是只知道是什么不知道為什么,停留在照貓畫虎的水平上還是挺容易出問題的。所以舉例子之前,先從jvm的實現原理上有個了解應當是不無裨益的。jvm的結構圖可以從《深入java虛擬機》這本巨牛的書上找到,絕對有權威性。從jvm的結構圖上可以看出來,jvm在實現的時候將屬於它的內存分為五部分,其中程序代碼(嚴格的說應當是字節碼)是放在java棧的棧幀中,而對象是從堆中分配的,堆這個東西我看可以理解成“對象池”。程序和程序中需要用到的對象放在兩個相對獨立的區域中,那么程序怎么使用對象呢?答案是程序中真正使用對象的地方其實只是聲明了一個對象的引用,也就是把堆中分配了的相應對象的地址放到引用中,棧和堆之間就是通過一個一個的引用來聯系的。引用嘛,我理解就是一個指針常量,指針常量又是個什么東西呢?說白了,就是一個無符號整數,這個整數所表達的是引用對象的地址。好了,這下清楚了,不管是基本類型變量(int,float,double什么的)還是對象,相應的內存地址中存放的都是一個數(無符號整數,整數,浮點數等)。傳遞參數的時候傳遞的就是相應內存地址中的數,所以說“ava函數是傳值的”。當然,這個數對於基本類型和對象類型來說意義是不一樣的,對於基本類型這個數就是其值本身,傳遞值的結果就是,改變新的變量的值不影響舊的變量的值;而對於對象來說這個數是它的地址,傳遞這個值就相當於傳遞了真實對象的引用,傳遞了引用或者說是地址的結果就是變化會全局可見,所以又可以說“java函數傳遞的參數是對象的引用”。
經過上面這一小堆討論,不難理解為什么java在傳遞參數時對於基本類型和對象表現不同。下面以具體實例來說明,看看是不是比原來沒有上面的解釋的時候好理解一點?

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 {
private String name;
private int id;
public ValueObject() {
}
public ValueObject(String name, int id) {
this.name = name;
this.id = 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;
}
}
java中對象的每個實例(就是對象)內存地址是唯一的,它一旦被創建,能夠對這個地址進行操作的就是其本身,如果ValueObject類中沒有public void setName之類的方法對這個類的實例中的數據進行修改的話,程序是沒有任何別的方法可以修改ValueObject類的實例中的數據,這個就是java的封裝特性。對於不提供修改內部數據的方法的類,我們稱為不可變(immutable)的類。在函數中對傳入的參數變量進行賦值操作,只能在函數范圍內改變局部變量指向的引用地址,但是不會改變原始地址的內容。因此,在changeValue2(...)函數內部的vo1和函數外的vo1雖然名字相同,但是實際上是不同的實例變量,只不過指向了和函數外的vo1同樣的地址,所以當我們用vo1=... 對其進行賦值的時候,只不過是把函數內的臨時變量指向了新的地址(就是從原來vo1的地址變到了另外一個地址),並沒有改變原始vo1內存地址中的內容。這就是在運行changeValue2(...)之后,vo1的值在main范圍內仍然沒有被修改的原因。而changeValue1里面是調用的ValueObject本身的function來更改其內容,因此是原始內存地址中的數據被更改了,所以是全局有效的.
總結:對於引用類型的傳參也是傳值的,傳的是引用類型的值,其實就是對象的地址。
1. java參數傳遞值的。
2. java所有對像變量都是對像的引用。
不知道解釋清楚沒,要是還是不能理解java參數傳遞的原理,強烈推薦看看《深入java虛擬機》一書的第五章,中文翻譯的不是特別出色,可以看看英文原版,應該比中文版好懂。