死磕面試系列,Java到底是值傳遞還是引用傳遞?
Java到底是值傳遞還是引用傳遞?
這雖然是一個(gè)老生常談的問(wèn)題,但是對(duì)于沒有深入研究過(guò)這塊,或者Java基礎(chǔ)不牢的同學(xué),還是很難回答得讓人滿意。
可能很多同學(xué)能夠很輕松的背出JVM、分布式事務(wù)、高并發(fā)、秒殺系統(tǒng)、領(lǐng)域模型等高難度問(wèn)題,但是對(duì)于Java基礎(chǔ)問(wèn)題不屑一顧。這種抓大放小的初衷是對(duì)的,要是碰到深究基礎(chǔ)細(xì)節(jié)的面試官,就抓瞎了。
今天一燈帶你一塊深入剖析Java傳遞的底層原理,看完這篇文章再去面試,面試官肯定要豎起大拇哥夸你:
“小伙子,你是懂Java傳遞的!”
1. 什么是形參和實(shí)參
形參: 就是形式參數(shù),用于定義方法的時(shí)候使用的參數(shù),是用來(lái)接收調(diào)用者傳遞的參數(shù)的。
實(shí)參: 就是實(shí)際參數(shù),用于調(diào)用時(shí)傳遞給方法的參數(shù)。實(shí)參在傳遞給別的方法之前是要被預(yù)先賦值的。
在Java方法調(diào)用的過(guò)程中,就是把實(shí)參傳遞給形參,形參的作用域在方法內(nèi)部。
2. 什么是值傳遞和引用傳遞
值傳遞: 是指在調(diào)用方法時(shí),將實(shí)際參數(shù)拷貝一份傳遞給方法,這樣在方法中修改形式參數(shù)時(shí),不會(huì)影響到實(shí)際參數(shù)。
引用傳遞: 也叫地址傳遞,是指在調(diào)用方法時(shí),將實(shí)際參數(shù)的地址傳遞給方法,這樣在方法中對(duì)形式參數(shù)的修改,將影響到實(shí)際參數(shù)。
也就是說(shuō)值傳遞,傳遞的是副本。引用傳遞,傳遞的是實(shí)際內(nèi)存地址。這是兩者的本質(zhì)區(qū)別,下面會(huì)用到。
3. 測(cè)試驗(yàn)證
3.1 基本數(shù)據(jù)類型驗(yàn)證
先用基本數(shù)據(jù)類型驗(yàn)證一下:
輸出結(jié)果:
可以看到雖然update方法修改了形參count的值,但是main方法中實(shí)參count的值并沒有變,但是為什么沒有變?我們深究一下底層原理。
我們都知道Java基本數(shù)據(jù)類型是存儲(chǔ)在虛擬機(jī)棧內(nèi)存中,棧中存放著棧幀,方法調(diào)用的過(guò)程,就是棧幀在棧中入棧、出棧的過(guò)程。
當(dāng)執(zhí)行main方法的時(shí)候,就往虛擬機(jī)棧中壓入一個(gè)棧幀,棧幀中存儲(chǔ)的局部變量信息是count=0。
當(dāng)執(zhí)行update方法的時(shí)候,再往虛擬機(jī)棧中壓入一個(gè)棧幀,棧幀中存儲(chǔ)的局部變量信息是count=0。
修改update棧幀中數(shù)據(jù),顯然不會(huì)影響到main方法棧幀的數(shù)據(jù)。
3.2 引用類型驗(yàn)證
再用引用類型數(shù)據(jù)驗(yàn)證一下:
輸出結(jié)果:
由代碼得知,update方法中重新初始化了user對(duì)象,并重新賦值,并不影響main方法中實(shí)參數(shù)據(jù)。
當(dāng)執(zhí)行main方法時(shí),會(huì)在堆內(nèi)存中開辟一塊內(nèi)存,在棧內(nèi)存中壓入一個(gè)棧幀,棧幀中存儲(chǔ)一個(gè)引用,指向堆內(nèi)存中的地址。
當(dāng)調(diào)用update方法時(shí),會(huì)把main方法的棧幀拷貝一份,再壓入棧內(nèi)存中,指向同一個(gè)堆內(nèi)存地址。
當(dāng)執(zhí)行update方法,重新初始化user對(duì)象,并重新賦值的時(shí)候。會(huì)在堆內(nèi)存中再開辟一塊內(nèi)存,再把棧內(nèi)存中update棧幀指向新的堆內(nèi)存地址,并修改新的堆內(nèi)存中的數(shù)據(jù)。
從這里可以看出是值傳遞,修改了形參里面數(shù)據(jù),實(shí)參并沒有跟著變化。
3.3 同一地址的引用類型驗(yàn)證
輸出結(jié)果:
可以看出update方法修改user對(duì)象的屬性,main方法中user對(duì)象也跟著變了。
這是不是說(shuō)明Java支持引用傳遞呢?
并不是。這里在參數(shù)傳遞的過(guò)程中,只是把實(shí)參的地址拷貝了一份傳遞給形參,update方法中只修改了參數(shù)地址里面的內(nèi)容,并沒有對(duì)形參本身進(jìn)行修改。
4. 總結(jié)
經(jīng)過(guò)上述分析,Java參數(shù)傳遞中,不管傳遞的是基本數(shù)據(jù)類型還是引用類型,都是值傳遞。
當(dāng)傳遞基本數(shù)據(jù)類型,比如原始類型(int、long、char等)、包裝類型(Integer、Long、String等),實(shí)參和形參都是存儲(chǔ)在不同的棧幀內(nèi),修改形參的棧幀數(shù)據(jù),不會(huì)影響實(shí)參的數(shù)據(jù)。
當(dāng)傳參的引用類型,形參和實(shí)參指向同一個(gè)地址的時(shí)候,修改形參地址的內(nèi)容,會(huì)影響到實(shí)參。當(dāng)形參和實(shí)參指向不同的地址的時(shí)候,修改形參地址的內(nèi)容,并不會(huì)影響到實(shí)參。