1. 程式人生 > >Java中只有值傳遞,沒有引用傳遞

Java中只有值傳遞,沒有引用傳遞

先來看一個作為程式設計師都熟悉的值傳遞的例子:

... ...
//定義了一個改變引數值的函式
public static void changeValue(int x) {
x = x *2;
}
... ...
//呼叫該函式
int num = 5;
System.out.println(num);
changeValue(num);
System.out.println(num);
... ...

答案顯而易見,呼叫函式changeValue()前後num的值都沒有改變。

由此做一個引子,我用圖表描繪一個值傳遞的過程:

這裡寫圖片描述

num作為引數傳遞給changeValue()方法時,是將記憶體空間中num所指向的那個儲存單元中存放的值,即”5”,傳送給了changeValue()方法中的x變數,而這個x變數也在記憶體空間中分配了一個儲存單元,這個時候,就把num的值5傳送給了這個儲存單元中。此後,在changeValue()方法中對x的一切操作都是針對x所指向的這個儲存單元,與num所指向的那個儲存單元沒有關係了!
自然,在函式呼叫之後,num所指向的儲存單元的值還是沒有發生變化,這就是所謂的“值傳遞”!
值傳遞的精髓是:傳遞的是儲存單元中的內容,而非地址或者引用!

接下來,就來看java中的物件引數是怎麼傳遞的:
同樣,先給出一段程式碼:

... ...
class person {
public static String name = "Jack";
... ...
}
... ...
//定義一個改變物件屬性的方法
public static void changeName(Person p) {
p.name = "Rose";
}
... ...
public static void main(String[] args) {
//定義一個Person物件,person是這個物件的引用
Person person = new Person();
//先顯示這個物件的name屬性
System.out.println(person.name); //呼叫changeName(Person p)方法 changeName(person); //再顯示這個物件的name屬性,看是否發生了變化 System.out.println(person.name); }

答案應該大家都心知肚明:
第一次顯示:“Jack”
第二次顯示:“Rose”

方法用了一個物件引數,該物件內部的內容就可以改變,我之前一直認為應該是該物件複製了一個引用副本給呼叫函式的引數,使得該方法可以對這個物件進行操作,其實是錯了!

Java 程式語言只有值傳遞引數。當一個物件例項作為一個引數被傳遞到方法中時,引數的值就是該物件的引用一個副本。指向同一個物件,物件的內容可以在被呼叫的方法中改變,但物件的引用(不是引用的副本)是永遠不會改變的。

為什麼這裡是“值傳遞”,而不是“引用傳遞”?
我還是用圖表描繪比較能解釋清楚:

這裡寫圖片描述

主函式中new 了一個物件Person,實際分配了兩個物件:新建立的Person類的實體物件,和指向該物件的引用變數person。

【注意:在java中,新建立的實體物件在堆記憶體中開闢空間,而引用變數在棧記憶體中開闢空間】

正如如上圖所示,左側是堆空間,用來分配記憶體給新建立的實體物件,紅色框是新建的Person類的實體物件,000012是該實體物件的起始地址;而右側是棧空間,用來給引用變數和一些臨時變數分配記憶體,新實體物件的引用person就在其中,可以看到它的儲存單元的內容是000012,記錄的正是新建Person類實體物件的起始地址,也就是說它指向該實體物件。

這時候,好戲上臺了:

呼叫了changeName()方法,person作為物件引數傳入該方法,但是大家特別注意,它傳入的是什麼!!!person引用變數將自己的儲存單元的內容傳給了changeName()方法的p變數!也就是將實體物件的地址傳給了p變數,從此,在changeName()方法中對p的一切操作都是針對p所指向的這個儲存單元,與person引用變數所指向的那個儲存單元再沒有關係了!

回顧一下上面的一個值傳遞的例子,值傳遞,就是將儲存單元中的內容傳給呼叫函式中的那個引數,這裡是不是異曲同工,是所謂“值傳遞”,而非“引用傳遞”!!!

那為什麼物件內部能夠發生變化呢?

那是因為:p所指向的那個儲存單元中的內容是實體物件的地址,使得p也指向了該實體物件,所以才能改變物件內部的屬性!
這也是我們大多數人會誤以為是“引用傳遞”的終極原因!!!