Java的參數傳遞是「值傳遞」仍是「引用傳遞」?

關於Java傳參時是引用傳遞仍是值傳遞,一直是一個討論比較多的話題。java

有人說Java中只有值傳遞,也有人說值傳遞和引用傳遞都是存在的,比較容易讓人產生疑問。數組

關於值傳遞和引用傳遞其實須要分狀況看待。函數

1、Java數據類型

咱們都知道,Java數據類型分爲「基本數據類型」和「引用類型」兩大類。this

  • 基本數據類型(8種)

  • 引用類型(3種)

  引用類型可分爲類引用類型(類)、接口引用類型(接口)和數組引用類型(數組)。如下代碼定義了3個引用變量:user、myThread、intArray:spa

User user; java.lang.Runnable myThread; int[] intArray;

  其中user變量位類引用類型,myThread變量爲接口引用類型,而intArray變量爲數組引用類型。myThread變量之因此是接口引用類型,是由於java.lang.Runnable是接口,而不是類。code

  類引用類型的變量引用這個類或者其子類的實例,接口引用類型的變量引用實現了這個接口的類的實例,數組引用類型的變量引用引用這個數組類型的實例。在Java語言中,數組也被看做對象。因而可知,無論是何種引用類型的變量,他們引用的都是對象。對象

  若是一個引用類型變量不引用任何對象,那麼能夠把它賦值爲null。在初始化一個引用類型變量時,經常給它賦初值爲null。blog

User user = null;

方法的參數分爲實際參數和形式參數。接口

  • 實際參數:調用方法時寫的具體數值。
  • 形式參數:定義方法時寫的參數。

  通常狀況下,在數據作爲參數傳遞的時候,基本數據類型是值傳遞,引用數據類型是引用傳遞(地址傳遞)。內存

值傳遞

 public static void main(String[] args) { int num1 = 100; int num2 = 200; take(num1, num2); System.out.println("num1 = " + num1); System.out.println("num2 = " + num2); } public static void take(int a, int b) { int temp = a; a = b; b = temp; System.out.println("a = " + a); System.out.println("b = " + b); }

 運行結果:

a = 200 b = 100 num1 = 100 num2 = 200

流程

  1. 主函數進棧,num一、num2初始化。
  2. 調用take方法,take()進棧,將num1和num2的值複製一份給a和b。
  3. take方法中對a和b的值進行交換。
  4. take方法運行完畢,a、b的值已經交換。
  5. take方法彈棧。
  6. 主函數彈棧。

解析

  在take方法中,a、b的值進行交換,並不會影響到num1和num2。由於a、b中的值只是從num一、num2中複製過來的。也就是說a、b至關於num一、num2的副本,副本的內容不管怎麼修改,都不會影響到原件自己。

引用傳遞

 public static void main(String[] args) { int[] intArray = {1,2,3,4,5}; change(intArray); System.out.println(intArray[0]); } public static void change(int[] array) { int len = array.length; array[0] = 0; }

運行結果:

0 

流程

  1. 主函數進棧,int[] intArray初始化。
  2. 調用change方法,change()進棧,將intArray的地址值,複製一份給array。
  3. change方法中,根據地址值,找到堆中的數組,並將第一個元素的值改成0。
  4. change方法運行完畢,數組中第一個元素的值已經改變。
  5. change方法彈棧。
  6. 主函數彈棧。

解析

  調用change()的時候,形參array接收的是intArray地址值的副本。並在change方法中,經過地址值,對數組進行操做。change方法彈棧之後,數組中的值已經改變。main方法中,打印出來的intArray[0]也就從原來的1變成了0。

  不管是主函數,仍是change方法,操做的都是同一個地址值對應的數組。 

  就像你把本身家的鑰匙給了一把給保姆,保姆拿到鑰匙在你家打掃衛生,結束後走了。等你拿着鑰匙回到家之後,家裏已經面目一新。這裏的鑰匙就至關於地址值,家就至關於數組自己。

String類型傳遞

    public static void main(String[] args) {
        String str = "ABC";
        change(str);
        System.out.println(str);
    }
    public static void change(String a) {
        a = "DEF";
    }

運行結果:

ABC

  這就奇怪了。String是一個類,類是類引用類型,做爲參數傳遞的時候,應該是引用傳遞。可是從結果看起來倒是值傳遞。

緣由

String的Api文檔中有這麼一句話:

意思是String的值在建立以後不能被改變。

Api中還有一段:

意思是

String str = "abc"; 
至關於:
char data[] = {'a', 'b', 'c'}; 
String str = new String(data); 

  也就是說對String類型的str的任何修改至關於從新建立一個對象,並將新的地址值賦給str。這樣的話,上面的代碼就能夠寫成這樣:

    public static void main(String[] args) {
        String str1 = "ABC";
        change(str1);
        System.out.println(str1);
    }

    public static void change(String a) {
        char data[] = {'D', 'E', 'F'};
        String str = new String(data);
        a = str;
    }

流程

  1. 主函數進棧,str1初始化。
  2. 調用change方法,change()進棧,將str1的地址值複製一份給a。
  3. change方法中,從新建立一個String對象"DEF",並將a指向了新的地址值。
  4. change方法執行完畢,a指向的地址值已經發生改變。
  5. change方法彈棧。
  6. 主函數彈棧。

解析

  String對象做爲參數傳遞時,走的依然是引用傳遞,只不過String這個類比較特殊。String對象一旦建立,內容便不可更改,每一次內容的更改都是從新建立出來的新對象。當change方法執行完畢時,a所指向的地址值已經發生改變,而a原本的地址值就是複製過來的副本,因此並不能改變str1的值。

String類型相似狀況

class User {
    String name;
    public User(String name) {
        this.name = name;
    }
}

    public static void main(String[] args) {
        User user = new User("張三");
        change(user);
        System.out.println(user.name);
    }

    public static void change(User u) {
        User user = new User("李四");
        u = user;
    }

運行結果:

張三

總結

  • Java基本數據類型傳遞參數時是值傳遞;引用類型傳遞參數時是引用傳遞。
  • 值傳遞時,將實參的傳遞一份給形參;引用傳遞時,將實參的地址值傳遞一份給形參。
  • 值傳遞時,實參把它的傳遞給對應的形參,函數接收的是原始值的一個拷貝,此時內存中存在兩個相等的基本類型,即實參和形參後面方法中的操做都是對形參這個值的修改,不影響實參的值。引用傳遞時,實參的引用(地址,而不是參數的值)被傳遞給方法中相對應的形參,函數接收的是原始值的內存地址;在方法執行中,形參和實參內容相同,指向同一塊內存地址,方法執行中對引用的操做將會影響到實際對象。
  • 須要特殊考慮String,以及Integer、Double等幾個基本類型包裝類,它們都是immutable類型,由於沒有提供自身修改的函數,每次操做都是新建立一個對象,因此要特殊對待。由於最後的操做不會修改實參,能夠認爲是和基本數據類型類似,爲值傳遞。
相關文章
相關標籤/搜索