1. 程式人生 > >關於Java物件作為引數傳遞是傳值還是傳引用的問題

關於Java物件作為引數傳遞是傳值還是傳引用的問題

前言

  在Java中,當物件作為引數傳遞時,究竟傳遞的是物件的值,還是物件的引用,這是一個飽受爭議的話題。若傳的是值,那麼函式接收的只是實參的一個副本,函式對形參的操作並不會對實參產生影響;若傳的是引用,那麼此時對形參的操作則會影響到實參。
  首先我們來看一句程式碼:

Object obj = new Object();

  這句話的意思是:建立一個Object物件,再建立一個名為obj的引用,讓這個引用指向這個物件,如下圖所示:

  1.png-4.3kB

在有了上面的基礎之後,我們便來看下面這組在網上很流行的例子:

基本資料型別作為引數傳遞:

例1:

public class test {
    public static void main(String[] args) {
        int i = 1;
        System.out.println("before change, i = "+i);
        change(i);
        System.out.println("after change, i = "+i);
    }
    public static void change(int i){
        i = 5;
    }
}

  這個例子不難理解,當基本資料型別作為引數傳遞時,傳遞的是實參值的副本,即傳的是值,無論在函式中怎麼操作這個副本,實參的值是不會被改變的。所以以上程式碼執行的結果是:
  
  before change, i = 1
  after change, i = 1
  
  

物件作為引數傳遞:

  在下面的例2中,我們把StringBuffer物件作為引數傳遞到change函式。
  
  例2:

public class test {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("Hello ");
        System.out.println("before change, sb is "+sb.toString());
        change(sb);
        System.out.println("after change, sb is "+sb.toString());
    }
    public static void change(StringBuffer stringBuffer){
        stringBuffer.append("world !");
    }
}

  為了方便推理出結論,我們先直接看程式的執行結果:
  
  before change, sb is Hello
  after change, sb is Hello world !
  
  從輸出結果中我們可以發現,sb所指向的物件的值被改變了,那麼是否我們可以推論出,在Java中,當物件作為引數傳遞時,傳遞的是該物件的引用呢?我們再來看下面這個例子:
  
  例3:

public class test {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("Hello ");
        System.out.println("before change, sb is "+sb.toString());
        change(sb);
        System.out.println("after change, sb is "+sb.toString());
    }
    public static void change(StringBuffer stringBuffer){
        stringBuffer = new StringBuffer("Hi ");
        stringBuffer.append("world !");
    }
}

  如果上面的推論是正確的,即Java中物件作為引數傳遞,實際傳遞的是該物件的引用,那麼在呼叫change函式之後,原物件的值應該是會改變的,變為“Hi world !”,但是,當我們執行程式後,結果卻是如下所示:
  
  before change, sb is Hello
  after change, sb is Hello
  
  原物件的值並沒有被改變,這與上面的推論相矛盾!為什麼在Java中,當物件作為引數傳遞時,有的時候實參被改變了,而有的時候實參並未被改變呢?下面讓我們來分析一下其中的原因:
  從文章的開頭我們知道,當執行StringBuffer sb = new StringBuffer(“Hello “)時,我們建立了一個指向新建物件“new StringBuffer(“Hello “)”的引用“sb”,如下圖所示:
  
  2.png-3.2kB
  
  在例2中,當我們呼叫change函式後,實際上,形參stringBuffer也指向了實參sb所指向的物件,即:
  
  3.png-6.3kB
  
  那麼當我們執行stringBuffer.append(“world !”)後,便通過物件的引用“stringBuffer”修改了物件的值,使之變成了“Hello world !”,即:
  
  4.png-6.6kB
  
  但是,在例3中的change函式中,我們又新建了一個物件“new StringBuffer(“Hi “)”(這實際上在記憶體中開闢了一塊在原物件地址之外的新區域),這讓形參stringBuffer實際指向了這個新建的物件,並將新物件的值設定為“Hi world !”,即:
  
  image_1art4suka65r1m4o1a0spgcjr89.png-10.1kB
  
  那麼我們就不難理解,為何在執行完change函式之後,實參的值仍為“Hello”了。
  

結論

  綜上所述,我們可以得出結論:在Java中,當物件作為引數傳遞時,實際上傳遞的是一份“引用的拷貝”。