如題
引用:https://www.zhihu.com/question/31203609
作者:Intopass
鏈接:https://www.zhihu.com/question/31203609/answer/50992895
來(lái)源:知乎
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)接奈,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處考余。
首先痊土,不要糾結(jié)于 Pass By Value 和 Pass By Reference 的字面上的意義信姓,否則很容易陷入所謂的“一切傳引用其實(shí)本質(zhì)上是傳值”這種并不能解決問(wèn)題無(wú)意義論戰(zhàn)中。
更何況惊畏,要想知道Java到底是傳值還是傳引用扰法,起碼你要先知道傳值和傳引用的準(zhǔn)確含義吧?可是如果你已經(jīng)知道了這兩個(gè)名字的準(zhǔn)確含義衅鹿,那么你自己就能判斷Java到底是傳值還是傳引用撒踪。
這就好像用大學(xué)的名詞來(lái)解釋高中的題目,對(duì)于初學(xué)者根本沒(méi)有任何意義大渤。
一:搞清楚 基本類(lèi)型 和 引用類(lèi)型的不同之處
int num = 10;
String str = "hello";
<noscript>[圖片上傳失敗...(image-2771a3-1612023376862)]
</noscript>
[圖片上傳失敗...(image-c2c91c-1612023376863)]
如圖所示制妄,num是基本類(lèi)型,值就直接保存在變量中泵三。而str是引用類(lèi)型耕捞,變量中保存的只是實(shí)際對(duì)象的地址。一般稱(chēng)這種變量為"引用"切黔,引用指向?qū)嶋H對(duì)象砸脊,實(shí)際對(duì)象中保存著內(nèi)容。
二:搞清楚賦值運(yùn)算符(=)的作用
num = 20;
str = "java";
<noscript>[圖片上傳失敗...(image-a069cb-1612023376862)]
</noscript>
[圖片上傳失敗...(image-1b0bcb-1612023376863)]
對(duì)于基本類(lèi)型 num 纬霞,賦值運(yùn)算符會(huì)直接改變變量的值凌埂,原來(lái)的值被覆蓋掉。
對(duì)于引用類(lèi)型 str诗芜,賦值運(yùn)算符會(huì)改變引用中所保存的地址瞳抓,原來(lái)的地址被覆蓋掉。但是原來(lái)的對(duì)象不會(huì)被改變(重要)伏恐。
如上圖所示孩哑,"hello" 字符串對(duì)象沒(méi)有被改變。(沒(méi)有被任何引用所指向的對(duì)象是垃圾翠桦,會(huì)被垃圾回收器回收)
三:調(diào)用方法時(shí)發(fā)生了什么横蜒?參數(shù)傳遞基本上就是賦值操作胳蛮。
第一個(gè)例子:基本類(lèi)型
void foo(int value) {
value = 100;
}
foo(num); // num 沒(méi)有被改變
第二個(gè)例子:沒(méi)有提供改變自身方法的引用類(lèi)型
void foo(String text) {
text = "windows";
}
foo(str); // str 也沒(méi)有被改變
第三個(gè)例子:提供了改變自身方法的引用類(lèi)型
StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
builder.append("4");
}
foo(sb); // sb 被改變了,變成了"iphone4"丛晌。
第四個(gè)例子:提供了改變自身方法的引用類(lèi)型仅炊,但是不使用,而是使用賦值運(yùn)算符澎蛛。
StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
builder = new StringBuilder("ipad");
}
foo(sb); // sb 沒(méi)有被改變抚垄,還是 "iphone"。
重點(diǎn)理解為什么谋逻,第三個(gè)例子和第四個(gè)例子結(jié)果不同呆馁?
下面是第三個(gè)例子的圖解:
<noscript>[圖片上傳失敗...(image-6971dc-1612023376862)]
</noscript>
[圖片上傳失敗...(image-d11658-1612023376863)]
builder.append("4")之后
<noscript>[圖片上傳失敗...(image-32a5e9-1612023376862)]
</noscript>
[圖片上傳失敗...(image-48f5a8-1612023376863)]
下面是第四個(gè)例子的圖解:
<noscript>[圖片上傳失敗...(image-4468af-1612023376862)]
</noscript>
[圖片上傳失敗...(image-74d3eb-1612023376863)]
builder = new StringBuilder("ipad"); 之后
<noscript>[圖片上傳失敗...(image-b0a865-1612023376862)]
</noscript>
[圖片上傳失敗...(image-7584f7-1612023376863)]
2018年1月31日添加部分內(nèi)容:
這個(gè)答案點(diǎn)贊的不少,雖然當(dāng)時(shí)回答時(shí)并沒(méi)有講的特別詳細(xì)毁兆,今天就稍微多講一些各種類(lèi)型數(shù)據(jù)在內(nèi)存中的存儲(chǔ)方式浙滤。
從局部變量/方法參數(shù)開(kāi)始講起:
局部變量和方法參數(shù)在jvm中的儲(chǔ)存方法是相同的,都是在棧上開(kāi)辟空間來(lái)儲(chǔ)存的荧恍,隨著進(jìn)入方法開(kāi)辟瓷叫,退出方法回收。以32位JVM為例送巡,boolean/byte/short/char/int/float以及引用都是分配4字節(jié)空間摹菠,long/double分配8字節(jié)空間。對(duì)于每個(gè)方法來(lái)說(shuō)骗爆,最多占用多少空間是一定的次氨,這在編譯時(shí)就可以計(jì)算好。
我們都知道JVM內(nèi)存模型中有摘投,stack和heap的存在煮寡,但是更準(zhǔn)確的說(shuō),是每個(gè)線(xiàn)程都分配一個(gè)獨(dú)享的stack犀呼,所有線(xiàn)程共享一個(gè)heap幸撕。對(duì)于每個(gè)方法的局部變量來(lái)說(shuō),是絕對(duì)無(wú)法被其他方法外臂,甚至其他線(xiàn)程的同一方法所訪(fǎng)問(wèn)到的坐儿,更遑論修改。
當(dāng)我們?cè)诜椒ㄖ新暶饕粋€(gè) int i = 0宋光,或者 Object obj = null 時(shí)貌矿,僅僅涉及stack,不影響到heap罪佳,當(dāng)我們 new Object() 時(shí)逛漫,會(huì)在heap中開(kāi)辟一段內(nèi)存并初始化Object對(duì)象。當(dāng)我們將這個(gè)對(duì)象賦予obj變量時(shí)赘艳,僅僅是stack中代表obj的那4個(gè)字節(jié)變更為這個(gè)對(duì)象的地址酌毡。
數(shù)組類(lèi)型引用和對(duì)象:
當(dāng)我們聲明一個(gè)數(shù)組時(shí)克握,如int[] arr = new int[10],因?yàn)閿?shù)組也是對(duì)象阔馋,arr實(shí)際上是引用玛荞,stack上僅僅占用4字節(jié)空間,new int[10]會(huì)在heap中開(kāi)辟一個(gè)數(shù)組對(duì)象呕寝,然后arr指向它。
當(dāng)我們聲明一個(gè)二維數(shù)組時(shí)婴梧,如 int[][] arr2 = new int[2][4]下梢,arr2同樣僅在stack中占用4個(gè)字節(jié),會(huì)在內(nèi)存中開(kāi)辟一個(gè)長(zhǎng)度為2的塞蹭,類(lèi)型為int[]的數(shù)組孽江,然后arr2指向這個(gè)數(shù)組。這個(gè)數(shù)組內(nèi)部有兩個(gè)引用(大小為4字節(jié))番电,分別指向兩個(gè)長(zhǎng)度為4的類(lèi)型為int的數(shù)組岗屏。
<noscript>[圖片上傳失敗...(image-217454-1612023376862)]
</noscript>
[圖片上傳失敗...(image-83882a-1612023376863)]
所以當(dāng)我們傳遞一個(gè)數(shù)組引用給一個(gè)方法時(shí),數(shù)組的元素是可以被改變的漱办,但是無(wú)法讓數(shù)組引用指向新的數(shù)組这刷。
你還可以這樣聲明:int[][] arr3 = new int[3][],這時(shí)內(nèi)存情況如下圖
<noscript>[圖片上傳失敗...(image-660505-1612023376862)]
</noscript>
[圖片上傳失敗...(image-39a0f-1612023376863)]
你還可以這樣 arr3[0] = new int [5]; arr3[1] = arr2[0];
<noscript>[圖片上傳失敗...(image-4f8c47-1612023376862)]
</noscript>
[圖片上傳失敗...(image-eb1319-1612023376863)]
關(guān)于String:
原本回答中關(guān)于String的圖解是簡(jiǎn)化過(guò)的娩井,實(shí)際上String對(duì)象內(nèi)部?jī)H需要維護(hù)三個(gè)變量暇屋,char[] chars, int startIndex, int length。而chars在某些情況下是可以共用的洞辣。但是因?yàn)镾tring被設(shè)計(jì)成為了不可變類(lèi)型咐刨,所以你思考時(shí)把String對(duì)象簡(jiǎn)化考慮也是可以的。
String str = new String("hello")
<noscript>[圖片上傳失敗...(image-d06dc6-1612023376862)]
</noscript>
[圖片上傳失敗...(image-23e7b7-1612023376863)]
當(dāng)然某些JVM實(shí)現(xiàn)會(huì)把"hello"字面量生成的String對(duì)象放到常量池中扬霜,而常量池中的對(duì)象可以實(shí)際分配在heap中定鸟,有些實(shí)現(xiàn)也許會(huì)分配在方法區(qū),當(dāng)然這對(duì)我們理解影響不大著瓶。
?贊同 2069 ? ?176 條評(píng)論
?分享
?收藏 ?喜歡 收起?
繼續(xù)瀏覽內(nèi)容
知乎
發(fā)現(xiàn)更大的世界
打開(kāi)
Chrome
繼續(xù)
<meta itemprop="name" content="Hollis"><meta itemprop="image" content="https://pic1.zhimg.com/v2-01b118e2ec1f5543e6d919d3b169ab4e_l.jpg?source=1940ef5c"><meta itemprop="url" content="https://www.zhihu.com/people/hollis-11"><meta itemprop="zhihu:followerCount" content="3645"> [圖片上傳失敗...(image-66fabe-1612023376862)]
個(gè)人公眾號(hào):Hollis联予,技術(shù)公眾號(hào):Java之道;
934 人贊同了該回答
<meta itemprop="image"><meta itemprop="upvoteCount" content="934"><meta itemprop="url" content="https://www.zhihu.com/question/31203609/answer/576030121"><meta itemprop="dateCreated" content="2019-01-17T11:11:11.000Z"><meta itemprop="dateModified" content="2019-02-28T09:46:39.000Z"><meta itemprop="commentCount" content="104">
點(diǎn)贊蟹但、收藏還挺多躯泰,求一波關(guān)注
沒(méi)人邀請(qǐng),自己強(qiáng)行回答一波华糖。
關(guān)于這個(gè)問(wèn)題麦向,引發(fā)過(guò)很多廣泛的討論,看來(lái)很多程序員對(duì)于這個(gè)問(wèn)題的理解都不盡相同客叉,甚至很多人理解的是錯(cuò)誤的诵竭。還有的人可能知道Java中的參數(shù)傳遞是值傳遞话告,但是說(shuō)不出來(lái)為什么。
在開(kāi)始深入講解之前卵慰,有必要糾正一下大家以前的那些錯(cuò)誤看法了沙郭。如果你有以下想法,那么你有必要好好閱讀本文裳朋。
錯(cuò)誤理解一:值傳遞和引用傳遞病线,區(qū)分的條件是傳遞的內(nèi)容,如果是個(gè)值鲤嫡,就是值傳遞送挑。如果是個(gè)引用,就是引用傳遞暖眼。
錯(cuò)誤理解二:Java是引用傳遞惕耕。
錯(cuò)誤理解三:傳遞的參數(shù)如果是普通類(lèi)型,那就是值傳遞诫肠,如果是對(duì)象司澎,那就是引用傳遞。
實(shí)參與形參
我們都知道栋豫,在Java中定義方法的時(shí)候是可以定義參數(shù)的挤安。比如Java中的main方法,public static void main(String[] args)
笼才,這里面的args就是參數(shù)漱受。參數(shù)在程序語(yǔ)言中分為形式參數(shù)和實(shí)際參數(shù)。
形式參數(shù):是在定義函數(shù)名和函數(shù)體的時(shí)候使用的參數(shù),目的是用來(lái)接收調(diào)用該函數(shù)時(shí)傳入的參數(shù)骡送。
實(shí)際參數(shù):在調(diào)用有參函數(shù)時(shí)昂羡,主調(diào)函數(shù)和被調(diào)函數(shù)之間有數(shù)據(jù)傳遞關(guān)系。在主調(diào)函數(shù)中調(diào)用一個(gè)函數(shù)時(shí)摔踱,函數(shù)名后面括號(hào)中的參數(shù)稱(chēng)為“實(shí)際參數(shù)”虐先。
簡(jiǎn)單舉個(gè)例子:
public static void main(String[] args) {
ParamTest pt = new ParamTest();
pt.sout("Hollis");//實(shí)際參數(shù)為 Hollis
}
public void sout(String name) { //形式參數(shù)為 name
System.out.println(name);
}
實(shí)際參數(shù)是調(diào)用有參方法的時(shí)候真正傳遞的內(nèi)容,而形式參數(shù)是用于接收實(shí)參內(nèi)容的參數(shù)派敷。
值傳遞與引用傳遞
上面提到了蛹批,當(dāng)我們調(diào)用一個(gè)有參函數(shù)的時(shí)候,會(huì)把實(shí)際參數(shù)傳遞給形式參數(shù)篮愉。但是腐芍,在程序語(yǔ)言中,這個(gè)傳遞過(guò)程中傳遞的兩種情況试躏,即值傳遞和引用傳遞猪勇。我們來(lái)看下程序語(yǔ)言中是如何定義和區(qū)分值傳遞和引用傳遞的。
值傳遞(pass by value)是指在調(diào)用函數(shù)時(shí)將實(shí)際參數(shù)復(fù)制一份傳遞到函數(shù)中颠蕴,這樣在函數(shù)中如果對(duì)參數(shù)進(jìn)行修改泣刹,將不會(huì)影響到實(shí)際參數(shù)助析。
引用傳遞(pass by reference)是指在調(diào)用函數(shù)時(shí)將實(shí)際參數(shù)的地址直接傳遞到函數(shù)中,那么在函數(shù)中對(duì)參數(shù)所進(jìn)行的修改椅您,將影響到實(shí)際參數(shù)外冀。
有了上面的概念,然后大家就可以寫(xiě)代碼實(shí)踐了掀泳,來(lái)看看Java中到底是值傳遞還是引用傳遞 雪隧,于是,最簡(jiǎn)單的一段代碼出來(lái)了:
public static void main(String[] args) {
ParamTest pt = new ParamTest();
int i = 10;
pt.pass(10);
System.out.println("print in main , i is " + i);
}
public void pass(int j) {
j = 20;
System.out.println("print in pass , j is " + j);
}
上面的代碼中开伏,我們?cè)趐ass方法中修改了參數(shù)j的值膀跌,然后分別在pass方法和main方法中打印參數(shù)的值。輸出結(jié)果如下:
print in pass , j is 20
print in main , i is 10
可見(jiàn)固灵,pass方法內(nèi)部對(duì)name的值的修改并沒(méi)有改變實(shí)際參數(shù)i的值。那么劫流,按照上面的定義巫玻,有人得到結(jié)論:Java的方法傳遞是值傳遞。
但是祠汇,很快就有人提出質(zhì)疑了(哈哈仍秤,所以,不要輕易下結(jié)論咯可很。)诗力。然后,他們會(huì)搬出以下代碼:
public static void main(String[] args) {
ParamTest pt = new ParamTest();
User hollis = new User();
hollis.setName("Hollis");
hollis.setGender("Male");
pt.pass(hollis);
System.out.println("print in main , user is " + hollis);
}
public void pass(User user) {
user.setName("hollischuang");
System.out.println("print in pass , user is " + user);
}
同樣是一個(gè)pass方法我抠,同樣是在pass方法內(nèi)修改參數(shù)的值苇本。輸出結(jié)果如下:
print in pass , user is User{name='hollischuang', gender='Male'}
print in main , user is User{name='hollischuang', gender='Male'}
經(jīng)過(guò)pass方法執(zhí)行后,實(shí)參的值竟然被改變了菜拓,那按照上面的引用傳遞的定義瓣窄,實(shí)際參數(shù)的值被改變了,這不就是引用傳遞了么纳鼎。于是俺夕,根據(jù)上面的兩段代碼,有人得出一個(gè)新的結(jié)論:Java的方法中贱鄙,在傳遞普通類(lèi)型的時(shí)候是值傳遞劝贸,在傳遞對(duì)象類(lèi)型的時(shí)候是引用傳遞。
但是逗宁,這種表述仍然是錯(cuò)誤的映九。不信你看下面這個(gè)參數(shù)類(lèi)型為對(duì)象的參數(shù)傳遞:
public static void main(String[] args) {
ParamTest pt = new ParamTest();
String name = "Hollis";
pt.pass(name);
System.out.println("print in main , name is " + name);
}
public void pass(String name) {
name = "hollischuang";
System.out.println("print in pass , name is " + name);
}
上面的代碼輸出結(jié)果為
print in pass , name is hollischuang
print in main , name is Hollis
這又作何解釋呢?同樣傳遞了一個(gè)對(duì)象疙剑,但是原始參數(shù)的值并沒(méi)有被修改氯迂,難道傳遞對(duì)象又變成值傳遞了践叠?
Java中的值傳遞
上面,我們舉了三個(gè)例子嚼蚀,表現(xiàn)的結(jié)果卻不一樣禁灼,這也是導(dǎo)致很多初學(xué)者,甚至很多高級(jí)程序員對(duì)于Java的傳遞類(lèi)型有困惑的原因轿曙。
其實(shí)弄捕,我想告訴大家的是,上面的概念沒(méi)有錯(cuò)导帝,只是代碼的例子有問(wèn)題守谓。來(lái),我再來(lái)給大家畫(huà)一下概念中的重點(diǎn)您单,然后再舉幾個(gè)真正恰當(dāng)?shù)睦印?/p>
值傳遞(pass by value)是指在調(diào)用函數(shù)時(shí)將實(shí)際參數(shù)
復(fù)制
一份傳遞到函數(shù)中斋荞,這樣在函數(shù)中如果對(duì)參數(shù)
進(jìn)行修改,將不會(huì)影響到實(shí)際參數(shù)虐秦。
引用傳遞(pass by reference)是指在調(diào)用函數(shù)時(shí)將實(shí)際參數(shù)的地址直接
傳遞到函數(shù)中平酿,那么在函數(shù)中對(duì)參數(shù)
所進(jìn)行的修改当悔,將影響到實(shí)際參數(shù)括授。
那么直焙,我來(lái)給大家總結(jié)一下座慰,值傳遞和引用傳遞之前的區(qū)別的重點(diǎn)是什么屏积。
<u><noscript>[圖片上傳失敗...(image-3fde90-1612023376862)]
</noscript>
[圖片上傳失敗...(image-406ac4-1612023376862)]</u>
我們上面看過(guò)的幾個(gè)pass的例子中蒸甜,都只關(guān)注了實(shí)際參數(shù)內(nèi)容是否有改變却嗡。如傳遞的是User對(duì)象闰渔,我們?cè)囍淖兯膎ame屬性的值暮现,然后檢查是否有改變还绘。其實(shí),在實(shí)驗(yàn)方法上就錯(cuò)了送矩,當(dāng)然得到的結(jié)論也就有問(wèn)題了蚕甥。
為什么說(shuō)實(shí)驗(yàn)方法錯(cuò)了呢?這里我們來(lái)舉一個(gè)形象的例子栋荸。再來(lái)深入理解一下值傳遞和引用傳遞菇怀,然后你就知道為啥錯(cuò)了。
你有一把鑰匙晌块,當(dāng)你的朋友想要去你家的時(shí)候爱沟,如果你直接
把你的鑰匙給他了,這就是引用傳遞匆背。這種情況下呼伸,如果他對(duì)這把鑰匙做了什么事情,比如他在鑰匙上刻下了自己名字,那么這把鑰匙還給你的時(shí)候括享,你自己的鑰匙上也會(huì)多出他刻的名字搂根。
你有一把鑰匙,當(dāng)你的朋友想要去你家的時(shí)候铃辖,你復(fù)刻
了一把新鑰匙給他剩愧,自己的還在自己手里,這就是值傳遞娇斩。這種情況下仁卷,他對(duì)這把鑰匙做什么都不會(huì)影響你手里的這把鑰匙。
但是犬第,不管上面那種情況锦积,你的朋友拿著你給他的鑰匙,進(jìn)到你的家里歉嗓,把你家的電視砸了丰介。那你說(shuō)你會(huì)不會(huì)受到影響?而我們?cè)趐ass方法中鉴分,改變user對(duì)象的name屬性的值的時(shí)候基矮,不就是在“砸電視”么。
還拿上面的一個(gè)例子來(lái)舉例冠场,我們真正的改變參數(shù)
,看看會(huì)發(fā)生什么本砰?
public static void main(String[] args) {
ParamTest pt = new ParamTest();
User hollis = new User();
hollis.setName("Hollis");
hollis.setGender("Male");
pt.pass(hollis);
System.out.println("print in main , user is " + hollis);
}
public void pass(User user) {
user = new User();
user.setName("hollischuang");
user.setGender("Male");
System.out.println("print in pass , user is " + user);
}
上面的代碼中碴裙,我們?cè)趐ass方法中,改變了user對(duì)象点额,輸出結(jié)果如下:
print in pass , user is User{name='hollischuang', gender='Male'}
print in main , user is User{name='Hollis', gender='Male'}
我們來(lái)畫(huà)一張圖舔株,看一下整個(gè)過(guò)程中發(fā)生了什么,然后我再告訴你还棱,為啥Java中只有值傳遞载慈。
<u><noscript>[圖片上傳失敗...(image-482271-1612023376862)]
</noscript>
[圖片上傳失敗...(image-c2b5f3-1612023376862)]</u>
稍微解釋下這張圖,當(dāng)我們?cè)趍ain中創(chuàng)建一個(gè)User對(duì)象的時(shí)候珍手,在堆中開(kāi)辟一塊內(nèi)存办铡,其中保存了name和gender等數(shù)據(jù)。然后hollis持有該內(nèi)存的地址0x123456
(圖1)琳要。當(dāng)嘗試調(diào)用pass方法寡具,并且hollis作為實(shí)際參數(shù)傳遞給形式參數(shù)user的時(shí)候,會(huì)把這個(gè)地址0x123456
交給user稚补,這時(shí)童叠,user也指向了這個(gè)地址(圖2)。然后在pass方法內(nèi)對(duì)參數(shù)進(jìn)行修改的時(shí)候课幕,即user = new User();
厦坛,會(huì)重新開(kāi)辟一塊0X456789
的內(nèi)存五垮,賦值給user。后面對(duì)user的任何修改都不會(huì)改變內(nèi)存0X123456
的內(nèi)容(圖3)杜秸。
上面這種傳遞是什么傳遞放仗?肯定不是引用傳遞,如果是引用傳遞的話(huà)亩歹,在user=new User()
的時(shí)候匙监,實(shí)際參數(shù)的引用也應(yīng)該改為指向0X456789
,但是實(shí)際上并沒(méi)有小作。
通過(guò)概念我們也能知道亭姥,這里是把實(shí)際參數(shù)的引用的地址復(fù)制了一份,傳遞給了形式參數(shù)顾稀。所以达罗,上面的參數(shù)其實(shí)是值傳遞,把實(shí)參對(duì)象引用的地址當(dāng)做值傳遞給了形式參數(shù)静秆。
我們?cè)賮?lái)回顧下之前的那個(gè)“砸電視”的例子粮揉,看那個(gè)例子中的傳遞過(guò)程發(fā)生了什么。
<u><noscript>[圖片上傳失敗...(image-50d0e6-1612023376862)]
</noscript>
[圖片上傳失敗...(image-b0ed36-1612023376862)]</u>
同樣的抚笔,在參數(shù)傳遞的過(guò)程中扶认,實(shí)際參數(shù)的地址0X1213456
被拷貝給了形參,只是殊橙,在這個(gè)方法中辐宾,并沒(méi)有對(duì)形參本身進(jìn)行修改,而是修改的形參持有的地址中存儲(chǔ)的內(nèi)容膨蛮。
所以叠纹,值傳遞和引用傳遞的區(qū)別并不是傳遞的內(nèi)容。而是實(shí)參到底有沒(méi)有被復(fù)制一份給形參敞葛。在判斷實(shí)參內(nèi)容有沒(méi)有受影響的時(shí)候誉察,要看傳的的是什么,如果你傳遞的是個(gè)地址惹谐,那么就看這個(gè)地址的變化會(huì)不會(huì)有影響持偏,而不是看地址指向的對(duì)象的變化。就像鑰匙和房子的關(guān)系豺鼻。
那么综液,既然這樣,為啥上面同樣是傳遞對(duì)象儒飒,傳遞的String對(duì)象和User對(duì)象的表現(xiàn)結(jié)果不一樣呢谬莹?我們?cè)趐ass方法中使用name = "hollischuang";
試著去更改name的值,陰差陽(yáng)錯(cuò)的直接改變了name的引用的地址。因?yàn)檫@段代碼附帽,會(huì)new一個(gè)String埠戳,在把引用交給name,即等價(jià)于name = new String("hollischuang");
蕉扮。而原來(lái)的那個(gè)”Hollis”字符串還是由實(shí)參持有著的整胃,所以,并沒(méi)有修改到實(shí)際參數(shù)的值喳钟。
<u><noscript>[圖片上傳失敗...(image-bdf221-1612023376862)]
</noscript>
[圖片上傳失敗...(image-d990f2-1612023376862)]</u>
所以說(shuō)屁使,Java中其實(shí)還是值傳遞的,只不過(guò)對(duì)于對(duì)象參數(shù)奔则,值的內(nèi)容是對(duì)象的引用蛮寂。
總結(jié)
無(wú)論是值傳遞還是引用傳遞,其實(shí)都是一種求值策略(<u>Evaluation strategy</u>)易茬。在求值策略中酬蹋,還有一種叫做按共享傳遞(call by sharing)。其實(shí)Java中的參數(shù)傳遞嚴(yán)格意義上說(shuō)應(yīng)該是按共享傳遞抽莱。
按共享傳遞范抓,是指在調(diào)用函數(shù)時(shí),傳遞給函數(shù)的是實(shí)參的地址的拷貝(如果實(shí)參在棧中食铐,則直接拷貝該值)匕垫。在函數(shù)內(nèi)部對(duì)參數(shù)進(jìn)行操作時(shí),需要先拷貝的地址尋找到具體的值虐呻,再進(jìn)行操作年缎。如果該值在棧中,那么因?yàn)槭侵苯涌截惖闹盗蹇叮院瘮?shù)內(nèi)部對(duì)參數(shù)進(jìn)行操作不會(huì)對(duì)外部變量產(chǎn)生影響。如果原來(lái)拷貝的是原值在堆中的地址蜕该,那么需要先根據(jù)該地址找到堆中對(duì)應(yīng)的位置犁柜,再進(jìn)行操作。因?yàn)閭鬟f的是地址的拷貝所以函數(shù)內(nèi)對(duì)值的操作對(duì)外部變量是可見(jiàn)的堂淡。
簡(jiǎn)單點(diǎn)說(shuō)馋缅,Java中的傳遞,是值傳遞绢淀,而這個(gè)值萤悴,實(shí)際上是對(duì)象的引用。
而按共享傳遞其實(shí)只是按值傳遞的一個(gè)特例罷了皆的。所以我們可以說(shuō)Java的傳遞是按共享傳遞覆履,或者說(shuō)Java中的傳遞是值傳遞。