值傳遞OR引用傳遞?大部人都答錯(cuò)了!
Java 是值傳遞還是引用傳遞?這是 Java 中比較基礎(chǔ)的一道常見(jiàn)面試題,但對(duì)于這道問(wèn)題的大部分答案都是錯(cuò)的,大部人會(huì)這樣回答這個(gè)問(wèn)題:
在 Java 中,如果傳遞的是基本數(shù)據(jù)類型,那么就是值傳遞;而如果傳遞的是對(duì)象或數(shù)組的話,那么就是引用傳遞。
然而,這個(gè)答案是錯(cuò)的!
定義描述
值傳遞和引用傳遞是編程中參數(shù)傳遞給方法時(shí)的兩種方式,它們的定義如下:
- 值傳遞(Pass by Value):在值傳遞中,實(shí)際參數(shù)的值被復(fù)制一份,然后將這份復(fù)制的值傳遞給函數(shù)或方法的相應(yīng)參數(shù)。因此,函數(shù)或方法內(nèi)對(duì)參數(shù)所做的任何修改都不會(huì)影響到實(shí)際參數(shù)的值。
- 引用傳遞(Pass by Reference):在引用傳遞中,傳遞給方法的是實(shí)際參數(shù)的引用(或地址)。這意味著方法內(nèi)對(duì)參數(shù)所做的任何修改都會(huì)直接影響到實(shí)際參數(shù)。
需要注意的是,有些編程語(yǔ)言,如 C++ 提供了真正的引用傳遞機(jī)制,允許你直接傳遞變量的引用,并且可以在函數(shù)或方法中改變這個(gè)引用的指向。而在 Java 中,即使是對(duì)象,也是通過(guò)值傳遞的,只不過(guò)這個(gè)值是對(duì)象引用副本(而非對(duì)象引用本身)。
正確結(jié)論
在 Java 中,(傳遞參數(shù)時(shí))無(wú)論是基本數(shù)據(jù)類型還是對(duì)象(或數(shù)組),使用的都是值傳遞的方式。只是對(duì)于對(duì)象(或數(shù)組)而言,傳遞的值是對(duì)象引用副本,而非對(duì)象引用本身。
在 Java 中,只有值傳遞沒(méi)有引用傳遞。
舉個(gè)例子
例如 Integer 是包裝類對(duì)象吧?它不是基本數(shù)據(jù)類型對(duì)吧,當(dāng)我們傳遞 Integer 對(duì)象時(shí),在新方法所做的所有修改,并不會(huì)影響原對(duì)象本身,具體示例代碼如下:
public class PassExample {
public static void main(String[] args) {
Integer number = new Integer(10);
method(number);
System.out.println("number:" + number); // 輸出:number:10
}
public static void method(Integer number) {
number = 20; // 修改 num 的值,不會(huì)影響原始變量的值
}
}
以上程序的執(zhí)行結(jié)果如下:
圖片
“
從上述結(jié)果可以看出,當(dāng)傳遞的是 Integer 對(duì)象時(shí),其依然是值傳遞,所以在 Java 語(yǔ)言中,并沒(méi)有引用傳遞。
因此,無(wú)論是基礎(chǔ)數(shù)據(jù)類型,還是引用數(shù)據(jù)類型(對(duì)象),都為值傳遞,而非引用傳遞。
特殊的例子
有人說(shuō):不對(duì)啊,磊哥,你看我傳遞數(shù)組時(shí),改變傳遞的數(shù)組就會(huì)影響原數(shù)組啊,具體示例如下:
public class PassExample {
public static void main(String[] args) {
char[] name = {'磊', '哥'};
System.out.println("調(diào)用方法前:" + new String(name));
method(name);
System.out.println("調(diào)用方法后:" + new String(name));
}
private static void method(char[] n) {
n[1] = '神';
System.out.println("方法中修改為:" + new String(n));
}
}
以上程序的執(zhí)行結(jié)果為:
調(diào)用方法前:磊哥
方法中修改為:磊神
調(diào)用方法后:磊神
這樣就出問(wèn)題了,當(dāng)傳遞了數(shù)組之后,明顯是“引用傳遞”,而非值傳遞,這到底是怎么回事?
別著急,當(dāng)我們把新方法中的代碼做了以下調(diào)整之后,運(yùn)行結(jié)果又不一樣了,如下代碼所示:
public class PassExample {
public static void main(String[] args) {
char[] name = {'磊', '哥'};
System.out.println("調(diào)用方法前:" + new String(name));
method(name);
System.out.println("調(diào)用方法后:" + new String(name));
}
private static void method(char[] n) {
n = new char[2]; // 僅僅添加了此行代碼
n[1] = '神';
System.out.println("方法中修改為:" + new String(n));
}
}
以上程序的執(zhí)行結(jié)果為:
調(diào)用方法前:磊哥
方法中修改為: 神
調(diào)用方法后:磊哥
你會(huì)發(fā)現(xiàn),當(dāng)我們?cè)谛路椒ㄖ袃H僅添加了一行“n = new char[2];”代碼時(shí),它又變成了值傳遞,這是怎么回事?
原因分析
如果是引用傳遞,那么我在新方法中無(wú)論如何修改,那么都應(yīng)該是影響原對(duì)象才對(duì),而剛才我稍微調(diào)整了代碼之后就發(fā)現(xiàn)其并非引用傳遞,而是值傳遞,這是因?yàn)楫?dāng)傳遞數(shù)組時(shí),其傳遞的是“引用副本”,而非真正的引用對(duì)象(也就是其本身)。
也就說(shuō),當(dāng)傳遞數(shù)組時(shí),其實(shí)傳遞的是“引用副本”,如下圖所示:
圖片
然而,在調(diào)用了“n = new char[2];”代碼之后,給變量在堆上創(chuàng)建了新對(duì)象,此時(shí)就不再使用原來(lái)的引用副本了,這個(gè)時(shí)候,再修改新方法中的變量就不影響原變量了,如下圖所示:
圖片
所以,在 Java 中,只有值傳遞,它始終傳遞的都是副本,而非原(引用)對(duì)象。
小結(jié)
在 Java 中,(傳遞參數(shù)時(shí))無(wú)論是基本數(shù)據(jù)類型還是對(duì)象(或數(shù)組),使用的都是值傳遞的方式。只是對(duì)于對(duì)象(或數(shù)組)而言,傳遞的值是對(duì)象引用副本,而非對(duì)象引用本身。