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

今天,我在一本面試書上看到了關於java的一個參數傳遞的問題:寫道html

java中對象做爲參數傳遞給一個方法,究竟是值傳遞,仍是引用傳遞?java

 我毫無疑問的回答:「引用傳遞!」,而且還以爲本身對java的這一特性非常熟悉!程序員

結果發現,我錯了!面試

答案是:編程

值傳遞!Java中只有按值傳遞,沒有按引用傳遞!數組

回家後我就火燒眉毛地查詢了這個問題,以爲本身對java這麼基礎的問題都搞錯實在太丟人!編程語言

綜合網上的描述,我大概瞭解了是怎麼回事,如今整理以下,若有不對之處望大神提出!函數

先來看一個做爲程序員都熟悉的值傳遞的例子:spa

​
... ...
//定義了一個改變參數值的函數
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";
    //若是這麼寫:會發現打印出來的person.name是沒有發生變化的
    //p = new Person();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」

方法用了一個對象參數,該對象內部的內容就能夠改變,我以前一直認爲應該是該對象複製了一個引用副本給調用函數的參數,使得該方法能夠對這個對象進行操做,實際上是錯了!

http://www.cnblogs.com/clara/archive/2011/09/17/2179493.html 寫道

Java 編程語言只有值傳遞參數。當一個對象實例做爲一個參數被傳遞到方法中時,參數的值就是該對象的引用一個副本。指向同一個對象,對象的內容能夠在被調用的方法中改變,但對象的引用(不是引用的副本)是永遠不會改變的。 

爲何這裏是「值傳遞」,而不是「引用傳遞」?

我仍是用圖表描繪比較能解釋清楚:

 

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

【注意:在java中,新建立的實體對象在堆內存中開闢空間,而引用變量在棧內存中開闢空間】

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

這時候,好戲上臺了:

調用了changeName()方法,person做爲對象參數傳入該方法,可是你們特別注意,它傳入的是什麼!!!person引用變量將本身的存儲單元的內容傳給了changeName()方法的p變量!也就是將實體對象的地址傳給了p變量,今後,在changeName()方法中對p的一切操做都是針對p所指向的這個存儲單元,與person引用變量所指向的那個存儲單元再沒有關係了!

回顧一下上面的一個值傳遞的例子,值傳遞,就是將存儲單元中的內容傳給調用函數中的那個參數,這裏是否是殊途同歸,是所謂「值傳遞」,而非「引用傳遞」!!!

那爲何對象內部可以發生變化呢?

那是由於:p所指向的那個存儲單元中的內容是實體對象的地址,使得p也指向了該實體對象,因此才能改變對象內部的屬性!

這也是咱們大多數人會誤覺得是「引用傳遞」的終極緣由!!!

下面再舉個例子:

public class Example {
     String str = new String("good");
     char[] ch = { 'a', 'b', 'c' };
     public static void main(String args[]) {
           Example ex = new Example();
           ex.change(ex.str, ex.ch);
           System.out.print(ex.str + " and ");
           System.out.print(ex.ch);
     }
     public void change(String str, char ch[]) {
           str = "test ok";
           ch[0] = 'g';
    }
}

疑問:這段代碼爲何 str沒被改變,可是ch[0]卻改變了?

解釋:

(1)首先, String 和 char數組 都是引用類型,不是基本類型。

(2)傳進去的引用類型的參數(例如str),傳進去的時候至關於新建了一個變量var,已經不是原來的變量了,可是他們指向的數據區域都同樣(數據地址相同),因此若是你改變了str指向的數據區域,那也只是改變var的數據地址,沒有改變str的數據地址,str仍是指向原來的數據區域 。而 str = "test ok"; 這一句,就是改變了內部str指向的數據區域(改變數據地址),它再也不指向"good"對象,而是指向一個新對象"test ok",至關於str = new String("test ok");,這隻在函數內部有效,外部的str的數據地址沒有變,仍是指向原來"good"對象;而ch[0] = 'g';這一句,意思把內部ch指向的數據區域(也就是實際存放數組內容的地方)裏面的第一個字符改爲g,仍是在原來指向的數據區域上操做,並無改變內部ch的數據地址,因此這個修改也會反映到外部的ch。

(3)能夠試一下在函數裏面的 ch[0] = 'g'; 前面加多一句 ch = {'a','b','c'}; ,這時候就改變內部ch的數據地址了,它的內容雖然仍是abc,可是已經指向一個新的數據區域。你在外部再打印ch就會發現內容沒有改變。

解釋下:java內存的堆內存和棧內存

Java把內存分紅兩種,一種叫作棧內存,一種叫作堆內存

在函數中定義的一些基本類型的變量和對象的引用變量都是在函數的棧內存中分配。當在一段代碼塊中定義一個變量時,java就在棧中爲這個變量分配內存空間,當超過變量的做用域後,java會自動釋放掉爲該變量分配的內存空間,該內存空間能夠馬上被另做他用。

堆內存用於存放由new建立的對象和數組。在堆中分配的內存,由java虛擬機自動垃圾回收器來管理。在堆中產生了一個數組或者對象後,還能夠在棧中定義一個特殊的變量,這個變量的取值等於數組或者對象在堆內存中的首地址,在棧中的這個特殊的變量就變成了數組或者對象的引用變量,之後就能夠在程序中使用棧內存中的引用變量來訪問堆中的數組或者對象,引用變量至關於爲數組或者對象起的一個別名,或者代號。

引用變量是普通變量,定義時在棧中分配內存,引用變量在程序運行到做用域外釋放。而數組&對象自己在堆中分配,即便程序運行到使用new產生數組和對象的語句所在地代碼塊以外,數組和對象自己佔用的堆內存也不會被釋放,數組和對象在沒有引用變量指向它的時候,才變成垃圾,不能再被使用,可是仍然佔着內存,在隨後的一個不肯定的時間被垃圾回收器釋放掉。這個也是java比較佔內存的主要緣由,實際上,棧中的變量指向堆內存中的變量,這就是 Java 中的指針!

相關文章
相關標籤/搜索