Java Object 引用傳遞和值傳遞
@author ixenos
Java沒有引用傳遞:
除了在將參數傳遞給方法(或函數)的時候是"值傳遞",傳遞對象引用的副本,在任何用"="向引用對象變量賦值的時候也是傳遞引用的副本
參數傳遞,傳遞引用的副本,這看起來是引用傳遞,實則是傳遞了副本,這已經是值傳遞的概念了;
變量賦值,傳遞引用的副本,即對象地址值的副本
Java參數傳遞中沒有引用傳遞都是值傳遞
1.在 Java 應用程序中永遠不會傳遞對象,而只傳遞對象引用。因此是按引用傳遞對象。注意區分按引用傳遞對象和引用傳遞的區別,但重要的是要區分參數是如何傳遞的,這才是該節選的意圖。
2.Java 應用程序按引用傳遞對象這一事實並不意味着 Java 應用程序按引用傳遞參數。參數可以是對象引用,而 Java 應用程序是按值傳遞對象引用的。
3.Java 應用程序中的變量可以為以下兩種類型之一:引用類型或基本類型。
當作為參數傳遞給一個方法時,處理這兩種類型的方式是相同的。兩種類型都是按值傳遞的;沒有一種按引用傳遞。
4.按值傳遞意味着當將一個參數傳遞給一個函數時,函數接收的是原始值的一個副本。
基本類型傳遞的是值的副本,
引用類型傳遞的是引用的副本。
因此,如果函數修改了該參數,僅改變副本,而原始值保持不變。
按引用傳遞意味着當將一個參數傳遞給一個函數時,函數接收的是原始值的內存地址,是引用的副本而不是引用變量地址值的副本。
因此,如果函數修改了該參數,調用代碼中的原始值也隨之改變。
這就是引用值傳遞后還能通過引用調用對象內部方法的原因
C++和Java的差異
差異只在傳遞形參時:
當傳遞給函數的參數不是引用時,傳遞的都是該值的一個副本(按值傳遞)。
區別在於引用。
在 C++ 中當傳遞給函數的參數是引用時,您傳遞的就是這個引用,或者內存地址(按引用傳遞)。
在 Java 應用程序中,當對象引用是傳遞給方法的一個參數時,您傳遞的是該引用的一個副本(按值傳遞),而不是引用本身。
Java 應用程序按值傳遞所有參數,這樣就制作所有參數的副本,而不管它們的類型。
那么問題來了,Java中引用的副本實質是什么呢?
實質就是一份對象的地址,是新的引用變量(比如形參)的值!
副本改變, 原數據不會改變。
當一個引用的副本改變了引用所指向的地址中的屬性的時候 外邊看起來好像改變了原數據?
其實不然!
原數據是引用的值 即 該引用所指向的地址,也即對象的地址
副本的值也是這個地址,故副本可以通過這個地址改變這個地址中的數據,但不可改變這個地址的值!這就是值傳遞的奧義
但是如果改變了副本的值即讓副本指向一個新的地址的時候這是不會改變原引用的值的
原引用在這里是始終指向一個地址,不會隨副本的改變而變
具體參考:
作者:Intopass
鏈接:https://www.zhihu.com/question/31203609/answer/50992895
來源:知乎
著作權歸作者所有,轉載請聯系作者獲得授權。
一:搞清楚 基本類型 和 引用類型的不同之處
int num = 10; String str = "hello";
如圖所示,num是基本類型,值就直接保存在變量中。而str是引用類型,變量中保存的只是實際對象的地址。一般稱這種變量為"引用",引用指向實際對象,實際對象中保存着內容。
二:搞清楚賦值運算符(=)的作用
num = 20; str = "java";
對於基本類型 num ,賦值運算符會直接改變變量的值,原來的值被覆蓋掉。
對於引用類型 str,賦值運算符會改變引用中所保存的地址,原來的地址被覆蓋掉。 但是原來的對象不會被改變(重要)。
如上圖所示,"hello" 字符串對象沒有被改變。(沒有被任何引用所指向的對象是垃圾,會被垃圾回收器回收)
三:調用方法時發生了什么? 參數傳遞基本上就是賦值操作。
第一個例子:基本類型 void foo(int value) { value = 100; } foo(num); // num 沒有被改變 第二個例子:沒有提供改變自身方法的引用類型 void foo(String text) { text = "windows"; } foo(str); // str 也沒有被改變 第三個例子:提供了改變自身方法的引用類型 StringBuilder sb = new StringBuilder("iphone"); void foo(StringBuilder builder) { builder.append("4"); } foo(sb); // sb 被改變了,變成了"iphone4"。 第四個例子:提供了改變自身方法的引用類型,但是不使用,而是使用賦值運算符。 StringBuilder sb = new StringBuilder("iphone"); void foo(StringBuilder builder) { builder = new StringBuilder("ipad"); } foo(sb); // sb 沒有被改變,還是 "iphone"。
重點理解為什么,第三個例子和第四個例子結果不同?
下面是第三個例子的圖解:
builder.append("4")之后
下面是第四個例子的圖解:
![]()
builder = new StringBuilder("ipad"); 之后
![]()
