為什麼大家都說Java中只有值傳遞?

最近跟Java中的值傳遞和引用傳遞槓上了,一度懷疑人生。查了很多資料,加上自己的理解,終於搞清楚了,什麼是值傳遞和引用傳遞。也搞明白了,為什麼大家都說Java只有值傳遞,沒有引用傳遞。原來,我一直以來的認知都是錯誤的。。。

首先,需要了解一些概念性的東西。

形參與實參:

形參,是指在定義函數時使用的參數,目的是用於接收調用該函數時傳入的參數。簡單理解,就是所有函數(即方法)的參數都是形參。

實參,是指調用函數時,傳遞給函數的參數。

<code>public static void main(String[] args) {
    int num = 3;
    printVal(num); //這裡num是實參
}

private static void printVal(int num) {
    num = 5; //這裡num就是形參
}/<code>

值傳遞和引用傳遞

值傳遞:是指在調用函數時,將實際參數複製一份傳遞給函數,這樣在函數中修改參數時,不會影響到實際參數。其實,就是在說值傳遞時,只會改變形參,不會改變實參。

引用傳遞:是指在調用函數時,將實際參數的地址傳遞給函數,這樣在函數中對參數的修改,將影響到實際參數。

這裡,需要特別強調的是,千萬不要以為傳遞的參數是值就是值傳遞,傳遞的是引用就是引用傳遞。也不要以為傳遞的參數是基本數據類型就是值傳遞,傳遞的是對象就是引用傳遞。 這是大錯特錯的。以前的我,一直都是這樣認為的,現在想來真是太天真了。判斷是值傳遞還是引用傳遞的標準,和傳遞參數的類型是沒有一毛錢關係的。

下面三種情況,基本上可以涵蓋所有情況的參數類型。

當傳遞的參數是基本數據類型時:

<code>public class TestNum {
    public static void main(String[] args) {
        int num = 3;
        System.out.println("修改前的num值:"+num);
        changeValue(num);
        System.out.println("修改後的num值:"+num);
    }

    private static void changeValue(int num) {
        num = 5;
        System.out.println("形參num值:"+num);
    }
}/<code>

打印結果:

<code>修改前的num值:3
形參num值:5
修改後的num值:3/<code>

可以發現,傳遞基本數據類型時,在函數中修改的僅僅是形參,對實參的值的沒有影響。

需要明白一點,值傳遞不是簡單的把實參傳遞給形參,而是,實參建立了一個副本,然後把副本傳遞給了形參。下面用圖來說明一下參數傳遞的過程:

為什麼大家都說Java中只有值傳遞?

圖中num是實參,然後創建了一個副本temp,把它傳遞個形參value,修改value值對實參num沒有任何影響。

傳遞類型是引用類型時:

<code>public class User {
    private int age;
    private String name;
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public User(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public User() {
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
public class TestUser {
    public static void main(String[] args) {
        User user = new User(18, "zhangsan");
        System.out.println("修改對象前:"+user);
        changeUser(user);
        System.out.println("修改對象後:"+user);
    }

    private static void changeUser(User user) {
        user.setAge(20);
        user.setName("lisi");
    }
}/<code>

打印結果:

<code>修改對象前:User{age=18, name='zhangsan'}
修改對象後:User{age=20, name='lisi'}/<code>

可以發現,傳過去的user對象,屬性值被改變了。由於,user對象存放在堆裡邊,其引用存放在棧裡邊,其參數傳遞圖如下:

為什麼大家都說Java中只有值傳遞?

user是對象的引用,為實參,然後創建一個副本temp,把它傳遞給形參user1。但是,他們實際操作的都是堆內存中的同一個User對象。因此,對象內容的修改也會體現到實參user上。

傳遞類型是String類型(Integer等基本類型的包裝類等同)

<code>public class TestStr {
    public static void main(String[] args) {
        String str = new String("zhangsan");
        System.out.println("字符串修改前:"+str);
        changeStr(str);
        System.out.println("字符串修改後:"+str);
    }

    private static void changeStr(String str) {
        str = "lisi";
    }
}/<code>

打印結果:

<code>字符串修改前:zhangsan
字符串修改後:zhangsan/<code>

咦,看到這是不是感覺有點困惑。按照第二種情況,傳遞參數是引用類型時,不是可以修改對象內容嗎,String也是引用類型,為什麼在這又不變了呢?

再次強調一下,傳遞參數是引用類型,並不代表就是引用傳遞,其實它還是值傳遞。此時的 lisi 和上邊的 zhangsan 根本不是同一個對象。畫圖理解下:

為什麼大家都說Java中只有值傳遞?

圖中,str是對象 zhangsan 的引用,為實參,然後創建了一個副本temp,把它傳遞給了形參str1。此時,創建了一個新的對象 lisi ,形參str1指向這個對象,但是原來的實參str還是指向zhangsan。因此,形參內容的修改並不會影響到實參內容。所以,兩次打印結果都是zhangsan。

第三種情況和第二種情況雖然傳遞的都是引用類型變量,但是處理方式卻不一樣。第三種情況是創建了一個新的對象,然後把形參指向新對象,而第二種情況並沒有創建新對象,操作的還是同一個對象。如果把上邊changeUser方法稍作改變,你就會理解:

<code>private static void changeUser(User user) {
    //添加一行代碼,創建新的User對象
    user = new User();
    user.setAge(20);
    user.setName("lisi");
}/<code>

運行以上代碼,你就會驚奇的發現,最終打印修改前和修改後的內容是一模一樣的。這種情況,就等同於第三種情況。因為,這裡的形參和實參引用所指向的對象是不同的對象。因此,修改形參對象內容並不會影響實參內容。

<code>修改對象前:User{age=18, name='zhangsan'}
修改對象後:User{age=18, name='zhangsan'}/<code>

總結:

從以上三個例子中,我們就能理解了,為什麼Java中只有值傳遞,並沒有引用傳遞。值傳遞,不論傳遞的參數類型是值類型還是引用類型,都會在調用棧上創建一個形參的副本。不同的是,對於值類型來說,複製的就是整個原始值的複製。而對於引用類型來說,由於在調用棧中只存儲對象的引用,因此複製的只是這個引用,而不是原始對象。

最後,再次強調一下,傳遞參數是引用類型,或者說是對象時,並不代表它就是引用傳遞。引用傳遞不是用來形容參數的類型的,不要被“引用”這個詞本身迷惑了。這就如同我們生活中說的地瓜不是瓜,而是紅薯一樣。

  1. 參數傳遞時,是拷貝實參的副本,然後傳遞給形參。(值傳遞)
  2. 在函數中,只有修改了實參所指向的對象內容,才會影響到實參。以上第三種情況修改的實際上只是形參所指向的對象,因此不會影響實參。


分享到:


相關文章: