前段時間參加了一場面試,其中有一道引用傳遞的題,因為當時並沒有考慮清楚所以做錯了。
現在來復盤一下,題目如下:
private static void change(StringBuffer str11, StringBuffer str12) {
str12 = str11;
str11 = new StringBuffer("new world");
str12.append("new world");
}
public static void main(String[] args) {
StringBuffer str1 = new StringBuffer("good ");
StringBuffer str2 = new StringBuffer("bad ");
change(str1, str2);
System.out.println(str1.toString());
System.out.println(str2.toString());
}
good new world
bad
下面就來復盤一下為何是這兩個答案,下面會做詳細的圖文分析。
要搞明白這個問題,那么就需要明白Java中的值傳遞和引用傳遞了。
Java對象參數傳遞雖然傳遞的是地址(引用),但仍然是值調用。是時候需要給引用調用和值調用一個准確的定義了。
-
值調用(call by value): 在參數傳遞過程中,形參和實參占用了兩個完全不同的內存空間。形參所存儲的內容是實參存儲內容的一份拷貝。實際上,Java對象的傳遞就符合這個定義,只不過形參和實參所儲存的內容並不是常規意義上的變量值,而是變量的地址。咳,回過頭想想:變量的地址不也是一種值嗎!
-
引用調用(call by reference) : 在參數傳遞的過程中,形參和實參完全是同一塊內存空間,兩者不分彼此。 實際上,形參名和實參名只是編程中的不同符號,在程序運行過程中,內存中存儲的空間才是最重要的。不同的變量名並不能說明占用的內存存儲空間不同。
那么接下來就畫圖分析文章開頭拋出的問題:
這里再把代碼貼一遍,大家可以對比着代碼和圖一起看。
private static void change(StringBuffer str11, StringBuffer str12) {
str12 = str11;//2
str11 = new StringBuffer("new world");//3
str12.append("new world");//4
}//5
public static void main(String[] args) {
StringBuffer str1 = new StringBuffer("good ");
StringBuffer str2 = new StringBuffer("bad ");
change(str1, str2);//1
System.out.println(str1.toString());
System.out.println(str2.toString());
}
-
執行到第一步的圖:
-
執行到第二步的圖,這里chage方法中的形參str11,str22是實參str1,str2的地址拷貝。
-
這里str11和str22是實參str1,str2的地址拷貝,接着執行第三步的圖:
-
上圖中str12地址指向了“good”,接着看第四步圖:
-
上圖中str11的地址指向了堆中新的對象"new world",接着看第五步的圖:
這里圖算是畫完了,確實不太擅長畫圖,大家就先勉強看吧。
這里有一個很重要的點就是StringBuffer是可變的,具體解釋大家可以搜索下。
因為這里str12.append("new world")直接修改了good的值。所以main函數中打印的會是:good new world。
到這里就算講完了,一個很簡單的例子,這里算是對之前一道筆試題的復盤,有不對的地方歡迎大家指正。