關於java是值傳遞仍是引用傳遞

1、概念

實際上對這兩種傳遞方式,知乎上有個回答說得很好:app

值傳遞和引用傳遞,屬於函數調用時參數的求值策略(Evaluation Strategy),這是對調用函數時,求值傳值的方式的描述,而非傳遞的內容的類型(內容指:是值類型仍是引用類型,是值仍是指針)。iphone

值類型/引用類型,是用於區分兩種內存分配方式,值類型在調用棧上分配,引用類型在堆上分配。函數

一個描述內存分配方式,一個描述參數求值策略,二者之間無任何依賴或約束關係ui

實際上意思就是,在函數調用時,不要經過傳進去的參數類型去判斷是值傳遞仍是引用傳遞,而是要看這種語言具體的求值策略。lua

 

2、詳解

在函數調用過程當中,調用方提供實參,這些實參能夠是常量:spa

Call(1);             // 常量
Call(x);             // 變量
Call(2 * x + 1);     // 常量與變量的組合
Call(GetNumber());   // 對其它函數的調用

可是全部這些實參的形式,都統稱爲表達式(Expression)指針

求值(Evaluation)便是指對這些表達式的簡化並求解其值的過程。code

求值策略(值傳遞和引用傳遞)的關注的點在於,這些表達式在調用函數的過程當中,求值的時機值的形式的選取等問題。求值的時機,能夠是在函數調用前,也能夠是在函數調用後,由被調用者本身求值。這裏所謂調用後求值,能夠理解爲Lazy Load或On Demand的一種求值方式。對象

除了值傳遞和引用傳遞,還有一些其它的求值策略,詳見下表:blog

求值策略

求值時間

傳值方式

值傳遞(Pass by value)

調用前

值的結果(是原值的副本)

引用傳遞(Pass by reference)

調用前

原值(原始對象,無副本)

名傳遞(Pass by name)

調用後(用到才求值)

與值無關的一個名

重點是值傳遞和引用傳遞。上面給出的傳值方式的表述有些單薄,下表列出了一些兩者在行爲表象上的區別:

 

值傳遞

引用傳遞

根本區別

會建立副本(Copy)

不建立副本

結果

函數中沒法改變原始對象

函數中能夠改變原始對象

這裏的改變不是指mutate, 而是change,指把一個變量指向另外一個對象,而不是指僅僅改變屬性或是成員什麼的,因此說Java是Pass by value,緣由是它調用時Copy,實參不能指向另外一個對象,而不是由於被傳遞的東西本質上是個Value。

這些行爲,與參數類型是值類型仍是引用類型無關。對於值傳遞,不管是值類型仍是引用類型,都會在調用棧上建立一個副本,不一樣是,對於值類型而言,這個副本就是整個原始值的複製而對於引用類型而言,因爲引用類型的實例在堆中,在棧上只有它的一個引用(通常狀況下是指針),其副本也只是這個引用的複製,而不是整個原始對象的複製。

綜上所述,對於Java的函數調用方式最準確的描述是:參數藉由值傳遞方式,傳遞的值是個引用。(句中兩個「值」不是一個意思,第一個值是evaluation result,第二個值是value content)。

 

3、例子

1. 值類型與引用類型

int num = 10;
String str = "hello";

1

num是基本類型,值就直接保存在變量中。而str是引用類型,變量中保存的只是實際對象的地址。通常稱這種變量爲"引用",引用指向實際對象,實際對象中保存着內容,比較特別的是,string對象是不可變對象

 

2. 函數調用

1) 基本類型

void foo(int value) {
    value = 100;
}
foo(num); // num 沒有被改變

傳進foo()裏的只是num的一個副本。

2) 沒有提供改變自身方法的引用類型

void foo(String text) {
    text = "world";
}
str=」hello」
foo(str); // str 也沒有被改變

整個過程以下圖所示:

11

3) 提供了改變自身方法的引用類型

StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
    builder.append("4");
}
foo(sb); // sb指向內容被改變了,變成了"iphone4",但自己仍是指向一樣的地址。

append前:

2

append後:

3

4) 提供了改變自身方法的引用類型,可是不使用,而是使用賦值運算符

StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
    builder = new StringBuilder("ipad");
}
foo(sb); // sb指向內容沒有被改變,仍是 "iphone"。

賦值前:

4

賦值後:

5

 

4、參考

1. 知乎關於Java 究竟是值傳遞仍是引用傳遞的回答之一

2. 知乎關於Java 究竟是值傳遞仍是引用傳遞的回答之二

(完)

相關文章
相關標籤/搜索