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