摘要:我們在處新創(chuàng)建了一個并將其引用在處傳給了方法的參數(shù)該方法內(nèi)部引用在處被重新賦值。如果是引用傳遞,那么引用在處已經(jīng)被指向了新的輸出應(yīng)該為才對,事實上是怎樣的呢事實上輸出了也就是說方法改變了傳入引用所指對象的值。此處注意,并非將重新分配,而是。
我們來看一個新手甚至寫了多年Java的朋友都可能不是十分確定的問題:
在Java方法傳參時,究竟是引用傳遞還是值傳遞?
為了說明問題, 我給出一個非常簡單的class定義:
public class Foo { String attribute; Foo(String s) { this.attribute = s; } void setAttribute(String s) { this.attribute = s; } String getAttribute() { return this.attribute; } }
下面在闡明觀點時,可能會多次用到該類。
關(guān)于Java里值傳遞還是引用傳遞,至少從表現(xiàn)形式上來看,兩種觀點都有支撐的論據(jù)。下面我來一一分析:
觀點1:引用傳遞理由如下:
先看一段代碼
public class Main { public static void modifyReference(Foo c){ c.setAttribute("c"); // line DDD } public static void main(String[] args) { Foo fooRef = new Foo("a"); // line AAA modifyReference(fooRef); // line BBB System.out.println(fooRef.getAttribute()); // 輸出 c } }
上述示例,輸出結(jié)果為"c",而不是"a", 也就是傳入的fooRef里的屬性被修改了,發(fā)生了side-effect。
我們在line AAA處新創(chuàng)建了一個Object Foo并將其引用fooRef在line BBB處傳給了方法modifyReference()的參數(shù)cRef, 該方法內(nèi)部處理后,fooRef指向的Object中的值從"a"變成了"c", 而引用fooRef還是那個引用, 因此,我們是否可以認為,在line BBB處發(fā)生了引用傳遞?
先留著疑問,我們繼續(xù)往下看。
觀點2:值傳遞繼續(xù)看一段代碼
public class Main { public static void changeReference(Foo aRef){ Foo bRef = new Foo("b"); aRef = bRef; // line EEE } public static void main(String[] args) { Foo fooRef = new Foo("a"); // line AAA changeReference(fooRef); // line BBB System.out.println(fooRef.getAttribute()); // 輸出 a } }
上述示例,輸出結(jié)果為"a", 而不是"b", 即對傳入的fooRef內(nèi)部的change并沒有影響外部的傳入前的值。
我們在line AAA處新創(chuàng)建了一個Object Foo并將其引用fooRef在line EEE處傳給了方法changeReference()的參數(shù)aRef, 該方法內(nèi)部引用aRef在line DDD處被重新賦值。如果是引用傳遞,那么引用aRef在line EEE處已經(jīng)被指向了新的Object, 輸出應(yīng)該為"b"才對,事實上是怎樣的呢?事實上輸出了"a",也就是說changeReference()方法改變了傳入引用所指對象的值。
觀點1和觀點2的輸出結(jié)果多少會讓人有些困惑,別急,我們繼續(xù)往下看。
深入分析為了詳細分析這個問題,把上述兩段代碼合起來:
public class Main { public static void modifyReference(Foo cRef){ cRef.setAttribute("c"); // line DDD } public static void changeReference(Foo aRef){ Foo bRef = new Foo("b"); // line FFF aRef = bRef; // line EEE } public static void main(String[] args) { Foo fooRef = new Foo("a"); // line AAA changeReference(fooRef); // line BBB System.out.println(fooRef.getAttribute()); // 輸出 a modifyReference(fooRef); // line CCC System.out.println(fooRef.getAttribute()); // 輸出 c } }
下面來深入內(nèi)部來詳細分析一下引用和Object內(nèi)部的變化。
來看下面圖示:
① Line AAA, 申明一個名叫fooRef,類型為Foo的引用,并見其分配給一個新的包含屬性值為"f"的對象,該對象類型為Foo。
Foo fooRef = new Foo("a"); // line AAA
② Line DDD, 方法內(nèi)部,申明了一個Foo類型的名為aRef的引用,且aRef被初始化為null。
void changeReference(Foo a);
③ Line CCC, changeReference()方法被調(diào)用后,引用aRef被分配給fooRef指向的對象。
changeReference(fooRef);
④ Line FFF, 申明一個名叫bRef,類型為Foo的引用,并見其分配給一個新的包含屬性值為"b"的對象,該對象類型為Foo。
Foo bRef = new Foo("b");
⑤ Line EEE, 將引用aRef重新分配給了包含屬性"b"的對象。此處注意,并非將fooRef重新分配,而是aRef。
aRef = bRef;
⑥ Line CCC, 調(diào)用方法modifyReference(Foo cRef)后,新建了一個引用cRef并將之分配到包含該屬性"f"的對象上,該對象同時被兩個引用fooRef和cRef指向著。
modifyReference(fooRef);
⑦ Line DDD, cRef.setAttribute("c");將會改變cRef引用指向的包含屬性"f"的對象,而該對象同時被引用fooRef指向著。
cRef.setAttribute("c");
此時引用fooRef指向的對象內(nèi)部屬性值"f"也被重新設(shè)置為"c"。
總結(jié)Java內(nèi)部方法傳參不是引用傳遞,而是引用本身的"值"的傳遞,歸根結(jié)底還是值傳遞。將一個對象的引用fooRef傳給方法的形參newRef,將給該對象新增了一個引用,相當(dāng)于多了一個alias。我們可以通過這個原引用fooRef,或這是方法參數(shù)里的新引用newRef去訪問、操作原對象,也可以改變參數(shù)里的引用newRef本身的值,卻無法改變原引用fooRef的值。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/66038.html
面試舊敵之紅黑樹(直白介紹深入理解) - Android - 掘金 讀完本文你將了解到: 什么是紅黑樹 黑色高度 紅黑樹的 5 個特性 紅黑樹的左旋右旋 指定節(jié)點 x 的左旋 右圖轉(zhuǎn)成左圖 指定節(jié)點 y 的右旋左圖轉(zhuǎn)成右圖 紅黑樹的平衡插入 二叉查找樹的插入 插入后調(diào)整紅黑樹結(jié)構(gòu) 調(diào)整思想 插入染紅后... java 多線程同步以及線程間通信詳解 & 消費者生產(chǎn)者模式 & 死鎖 & Thread...
摘要:它對數(shù)組和對象使用按值傳遞,但這是在的共享傳參或拷貝的引用中使用的按值傳參。例如在這里,變量和值在執(zhí)行期間存儲在堆棧中。返回值這是可選的,函數(shù)可以返回值,也可以不返回值。變量被推入堆棧,從而在執(zhí)行時成為的副本。 這是專門探索 JavaScript 及其所構(gòu)建的組件的系列文章的第 22 篇。 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 如果你錯過了前面的章節(jié),可...
摘要:它對數(shù)組和對象使用按值傳遞,但這是在的共享傳參或拷貝的引用中使用的按值傳參。例如在這里,變量和值在執(zhí)行期間存儲在堆棧中。返回值這是可選的,函數(shù)可以返回值,也可以不返回值。變量被推入堆棧,從而在執(zhí)行時成為的副本。 這是專門探索 JavaScript 及其所構(gòu)建的組件的系列文章的第 22 篇。 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 如果你錯過了前面的章節(jié),可...
摘要:設(shè)計模式是以面向?qū)ο缶幊虨榛A(chǔ)的,的面向?qū)ο缶幊毯蛡鹘y(tǒng)的的面向?qū)ο缶幊逃行┎顒e,這讓我一開始接觸的時候感到十分痛苦,但是這只能靠自己慢慢積累慢慢思考。想繼續(xù)了解設(shè)計模式必須要先搞懂面向?qū)ο缶幊蹋駝t只會讓你自己更痛苦。 JavaScript 中的構(gòu)造函數(shù) 學(xué)習(xí)總結(jié)。知識只有分享才有存在的意義。 是時候替換你的 for 循環(huán)大法了~ 《小分享》JavaScript中數(shù)組的那些迭代方法~ ...
摘要:但有時候,當(dāng)我們的代碼只需要與父類打交道時,可以使用向上轉(zhuǎn)型,來使我們的代碼不依賴具體子類,比如以下代碼,方法可以接受類的任意子類內(nèi)存分析我們來分析以下轉(zhuǎn)型代碼在內(nèi)存中的表示 學(xué)習(xí)設(shè)計模式的時候,發(fā)現(xiàn)很多模式都用到了向上轉(zhuǎn)型(eg. 工廠方法)。而我對向上轉(zhuǎn)型(upcasting)的機制并不十分熟悉。這篇文章將深入分析向上轉(zhuǎn)型的機制、內(nèi)存分析。 概念 先從幾個基本概念開始: 1. Ja...
閱讀 2758·2021-11-19 09:40
閱讀 5332·2021-09-27 14:10
閱讀 2110·2021-09-04 16:45
閱讀 1489·2021-07-25 21:37
閱讀 3005·2019-08-30 10:57
閱讀 2990·2019-08-28 17:59
閱讀 1063·2019-08-26 13:46
閱讀 1415·2019-08-26 13:27