188W+程序員關(guān)注過的問題:Java到底是值傳遞還是引用傳遞?

在逛 Stack Overflow 的時(shí)候,發(fā)現(xiàn)了一些訪問量像阿爾卑斯山一樣高的問題,比如說這個(gè):Java 到底是值傳遞還是引用傳遞?訪問量足足有 188萬+,這不得了??!說明有很多很多的程序員被這個(gè)問題困擾過。實(shí)話實(shí)說吧,就是其中之一。

來回顧一下提問者的問題:

我一直認(rèn)為 Java 是按引用傳遞的,但是我看一些博客上說不是的。我就納悶了,Java 到底是值傳遞還是引用傳遞?值傳遞和引用傳遞有什么區(qū)別呢?

如果你也曾被這個(gè)問題困擾過,或者正在被困擾,就請(qǐng)隨我一起來梳理一下問題的答案。打怪進(jìn)階嘍!

01、值傳遞和引用傳遞

什么是值傳遞,什么是引用傳遞?我們需要先把這兩個(gè)定義搞清楚,才能搞清楚 Java 是按值傳遞還是按引用傳遞。

值傳遞(pass by value)是指在調(diào)用方法時(shí)將實(shí)參復(fù)制一份傳遞到方法中,這樣當(dāng)方法對(duì)形參進(jìn)行修改時(shí)不會(huì)影響到實(shí)參。

引用傳遞(pass by reference)是指在調(diào)用方法時(shí)將實(shí)參的地址直接傳遞到方法中,那么在方法中對(duì)形參所進(jìn)行的修改,將影響到實(shí)參。

上面是比較官方的定義,讀起來不免生硬。在我看來,值傳遞和引用傳遞的關(guān)鍵區(qū)別有兩點(diǎn):

1)調(diào)用方法時(shí)有沒有對(duì)實(shí)參進(jìn)行復(fù)制。

2)方法內(nèi)對(duì)形參的修改會(huì)不會(huì)影響到實(shí)參。

what?值傳遞和引用傳遞還沒有搞清楚,又來兩個(gè)新名詞:實(shí)參和形參。別急,別急。

02、實(shí)參和形參

實(shí)參和形參理解起來比值傳遞和引用傳遞容易的多,前者就好像是一元一次方程,后者就像是一元二次方程。

形參:定義方法名和方法體的時(shí)候使用的參數(shù),目的是用來接收調(diào)用該方法時(shí)傳入的參數(shù)。

實(shí)參:在調(diào)用有參方法時(shí)傳入的參數(shù),方法名后面的括號(hào)中的參數(shù)通常被稱為“實(shí)參”。

大家應(yīng)該都寫過“hello world”程序了,就像下面這樣。

public class Cmower {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}

其中 args 就相當(dāng)于是形參,而字符串“hello world”就相當(dāng)于是實(shí)參。如果覺得這個(gè)例子不容易理解,那再來看一個(gè)。

public class Cmower {
    public static void main(String[] args) {
        Cmower cmower = new Cmower();
        cmower.sop("沉默王二");
    }

    public void sop(String name) {
        System.out.println("hello " + name);
    }
}

其中“沉默王二”為實(shí)參;有參方法 sop(String name) 中的 name 為形參。形參就好像實(shí)參與被調(diào)用方法之間的一個(gè)橋梁,否則調(diào)用者沒法傳遞參數(shù),被調(diào)用的方法無法接收參數(shù)。

03、基本類型是值傳遞的

Java 中的數(shù)據(jù)類型可以分為兩種,一種是基本類型,一種是引用類型。我相信大家在看本篇文章之前,就能夠達(dá)成這樣一個(gè)共識(shí):基本類型是值傳遞的。這一點(diǎn)毫無疑問。

public class Cmower {
    public static void main(String[] args) {
        Cmower cmower = new Cmower();
        int age = 18;
        cmower.sop(age);
        System.out.println("main 中的 age " + age);
    }

    public void sop(int age) {
        age = 28;
        System.out.println("sop 中的 age " + age);
    }
}

上面這段代碼中,sop() 方法的實(shí)參 age 為 18,盡管 sop() 方法的形參被修改為 28,但并不會(huì)影響實(shí)參的值。這一點(diǎn)可以從輸出結(jié)果中加以證明。

sop 中的 age 28
main 中的 age 18

具體的執(zhí)行過程如下圖所示。

04、引用類型是值傳遞嗎?

大家之所以不確定 Java 是值傳遞的還是引用傳遞的,原因就出在這個(gè)引用類型上面。單從字面的意思上就容易搞混:引用類型不是引用傳遞難道還是值傳遞?

public class Cmower {
    private String name;

    public String getName() {
        return name;
    }

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

    public static void main(String[] args) {
        Cmower cmower = new Cmower();
        cmower.setName("沉默王二");
        cmower.sop(cmower);
        System.out.println("main 中的 cmower " + cmower.getName());
    }

    public void sop(Cmower cmower) {
        cmower.setName("沉默王三");
        System.out.println("sop 中的 cmower " + cmower.getName());
    }
}

main() 方法中,我們通過 new 關(guān)鍵字創(chuàng)建了一個(gè)對(duì)象 cmower,并將其 name 屬性設(shè)置為“沉默王二”;然后將實(shí)參 cmower 傳遞給 sop() 方法,在 sop() 方法中將形參 cmower 的 name 屬性修改為“沉默王三”。輸出結(jié)果是什么樣子呢?

sop 中的 cmower 沉默王三
main 中的 cmower 沉默王三

呀!實(shí)參 cmower 的屬性 name 竟然不是“沉默王二”而是“沉默王三”了!看看,看看,Java 不是值傳遞吧?

別急別急。我們?cè)?main 方法中追加幾行代碼。

Cmower cmower = new Cmower();
cmower.setName("沉默王二");

Cmower old = cmower;
cmower.sop(cmower);
System.out.println("main 中的 cmower " + cmower.getName());

System.out.println(old == cmower);

old == cmower 會(huì)是 true 還是 false 呢?閉上眼睛想一想。如果實(shí)在是想不出,拋一枚硬幣吧,反正不是 true 就是 false。假如引用類型是引用傳遞的,根據(jù)引用傳遞的定義(形參的修改將會(huì)影響到實(shí)參),那么結(jié)果一定就是 false。

我們來看一下輸出結(jié)果:

sop 中的 cmower 沉默王三
main 中的 cmower 沉默王三
true

true?開什么玩笑?

不好意思,沒有開玩笑。Java 的確是值傳遞的。只不過,引用類型在調(diào)用有參方法的時(shí)候,傳遞的是對(duì)象的引用,并不是對(duì)象本身。而對(duì)象的引用在傳遞的過程中并沒有發(fā)生改變,雖然對(duì)象本身發(fā)生了變化。可以通過下面這幅圖感受一下。

這下理解了吧?

05、總結(jié)

來看下面這段代碼。

int age = 18;
String name = "沉默王二";

age 是基本類型,所以值就直接保存在變量中;而 name 是引用類型,變量中保存的只是對(duì)象的內(nèi)存地址,這種變量一般稱之為對(duì)象的引用。

基本類型作為參數(shù)被傳遞時(shí)肯定是值傳遞;引用類型作為參數(shù)被傳遞時(shí)也是值傳遞,只不過“值”為對(duì)應(yīng)的引用。


好了各位讀者朋友們,以上就是本文的全部?jī)?nèi)容了。能看到這里的都是最優(yōu)秀的程序員,我必須要為大家點(diǎn)個(gè)贊??。如果覺得不過癮,還想看到更多,我再推薦幾篇給大家。

370W+程序員關(guān)注過的問題:如何比較 Java 的字符串?
250W+程序員關(guān)注過的問題:什么是 NullPointerException?
50W+程序員關(guān)注過的問題:為什么會(huì)發(fā)生ArrayIndexOutOfBoundsException?

有收獲?就點(diǎn)贊、留言,讓更多的人看到這篇文章。

如果想要第一時(shí)間看到我更新的文章,可以微信搜索「沉默王二」,關(guān)注我的公眾號(hào),回復(fù)「java」再送你一份精選電子書大禮包,包含這十年來我讀過的最優(yōu)質(zhì)的 Java 書籍。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容