值傳遞與引用傳遞
“哥,說說 Java 到底是值傳遞還是引用傳遞吧?”三妹一臉的困惑,看得出來她被這個問題折磨得不輕。
“說實在的,我在一開始學 Java 的時候也被這個問題折磨得夠嗆,總以為基本數據類型在傳參的時候是值傳遞,而引用類型是引用傳遞。”我對三妹袒露了心聲,為的就是讓她不再那么焦慮,她哥當年也是這么過來的。
C 語言是很多編程語言的母胎,包括 Java,那么對于 C 語言來說,所有的方法參數都是“通過值”傳遞的,也就是說,傳遞給被調用方法的參數值存放在臨時變量中,而不是存放在原來的變量中。這就意味著,被調用的方法不能修改調用方法中變量的值,而只能修改其私有變量的臨時副本的值。
Java 繼承了 C 語言這一特性,因此 Java 是按照值來傳遞的。
接下來,我們必須得搞清楚,到底什么是值傳遞(pass by value),什么是引用傳遞(pass by reference),否則,討論 Java 到底是值傳遞還是引用傳遞就顯得毫無意義。
當一個參數按照值的方式在兩個方法之間傳遞時,調用者和被調用者其實是用的兩個不同的變量——被調用者中的變量(原始值)是調用者中變量的一份拷貝,對它們當中的任何一個變量修改都不會影響到另外一個變量,據說 Fortran 語言是通過引用傳遞的。
“Fortran 語言?”三妹睜大了雙眼,似乎聽見了什么新的名詞。
“是的,Fortran 語言,1957 年由 IBM 公司開發(fā),是世界上第一個被正式采用并流傳至今的高級編程語言?!?/p>
當一個參數按照引用傳遞的方式在兩個方法之間傳遞時,調用者和被調用者其實用的是同一個變量,當該變量被修改時,雙方都是可見的。
“我們之所以容易搞不清楚 Java 到底是值傳遞還是引用傳遞,主要是因為 Java 中的兩類數據類型的叫法容易引發(fā)誤會,比如說 int 是基本類型,說它是值傳遞的,我們就很容易理解;但對于引用類型,比如說 String,說它也是值傳遞的時候,我們就容易弄不明白。”
我們來看看基本數據類型和引用數據類型之間的差別。
int age = 18;
String name = "二哥";
age 是基本類型,值就保存在變量中,而 name 是引用類型,變量中保存的是對象的地址。一般稱這種變量為對象的引用,引用存放在棧中,而對象存放在堆中。
這里說的棧和堆,是指內存中的一塊區(qū)域,和數據結構中的棧和堆不一樣。棧是由編譯器自動分配釋放的,所以適合存放編譯期就確定生命周期的數據;而堆中存放的數據,編譯器是不需要知道生命周期的,創(chuàng)建后的回收工作由垃圾收集器來完成。
“畫幅圖。”

當用 = 賦值運算符改變 age 和 name 的值時。
age = 16;
name = "三妹";
對于基本類型 age,賦值運算符會直接改變變量的值,原來的值被覆蓋。
對于引用類型 name,賦值運算符會改變對象引用中保存的地址,原來的地址被覆蓋,但原來的對象不會被覆蓋。

“三妹,注意聽,接下來,我們來說說基本數據類型的參數傳遞?!?/p>
Java 有 8 種基本數據類型,分別是 int、long、byte、short、float、double 、char 和 boolean,就拿 int 類型來舉例吧。
class PrimitiveTypeDemo {
public static void main(String[] args) {
int age = 18;
modify(age);
System.out.println(age);
}
private static void modify(int age1) {
age1 = 30;
}
}
1)main() 方法中的 age 為基本類型,所以它的值 18 直接存儲在變量中。
2)調用 modify() 方法的時候,將會把 age 的值 18 復制給形參 age1。
3)modify() 方法中,對 age1 做出了修改。
4)回到 main() 方法中,age 的值仍然為 18,并沒有發(fā)生改變。
如果我們想讓 age 的值發(fā)生改變,就需要這樣做。
class PrimitiveTypeDemo1 {
public static void main(String[] args) {
int age = 18;
age = modify(age);
System.out.println(age);
}
private static int modify(int age1) {
age1 = 30;
return age1;
}
}
第一,讓 modify() 方法有返回值;
第二,使用賦值運算符重新對 age 進行賦值。
“好了,再來說說引用類型的參數傳遞。”
就以 String 為例吧。
class ReferenceTypeDemo {
public static void main(String[] args) {
String name = "二哥";
modify(name);
System.out.println(name);
}
private static void modify(String name1) {
name1 = "三妹";
}
}
在調用 modify() 方法的時候,形參 name1 復制了 name 的地址,指向的是堆中“二哥”的位置。

當 modify() 方法調用結束后,改變了形參 name1 的地址,但 main() 方法中 name 并沒有發(fā)生改變。

總結:
Java 中的參數傳遞是按值傳遞的。 如果參數是基本類型,傳遞的是基本類型的字面量值的拷貝。 如果參數是引用類型,傳遞的是引用的對象在堆中地址的拷貝。
“好了,三妹,今天的學習就到這吧?!?/p>
PS:點擊「閱讀原文」可直達《教妹學Java》專欄的 GitHub 開源地址,記得 star 哦!
