最近看到一道關於String的面試題,差點讓我覺得String是值傳遞,就是下面這個例子,體驗下:<!-- more -->java
public class Demo{ public static void main(String[] args) { Demo d = new Demo(); String str = "BEA"; d.change(str); System.out.println(str); } void change(String s){ s= s.replace('A', 'E'); s = s.toLowerCase(); } }
當時一看到這個題目,我第一反應就是輸出」bee「,由於String是引用類型,其參數傳遞的方式就是引用傳遞,傳遞的是String的地址。但是答案讓個人大吃一驚,「BEA」,str根本就沒有發生變化!!面試
難道String是值傳遞?難道String是基本類型?segmentfault
其實都不是,後來經過查閱相應資料發現,jvm在實例化字符串時會使用字符串常量池,把str做爲參數傳入change()方法。jvm複製了一份str變量,爲了便於理解咱們叫它str'。這個時候str和str'都指向字符串常量池中的「abc」。app
當咱們執行s = s.replace('A', 'E');
其實至關於執行了s = new String(s.replace('A', 'E'));
jvm
要理解上面這兩段話,就要從java的底層結構提及了。java的內存模型大致分爲 堆 和 棧 (細分還有方法區,和程序計數器等)。ui
1.基本類型的變量放在棧裏;
2.封裝類型中,對象放在堆裏,對象的引用放在棧裏。
<font color=red>java在方法傳遞參數時,是將變量複製一份,而後傳入方法體去執行。</font>spa
根據這些再細分一下jvm的執行過程code
1.虛擬機在堆中開闢一塊內存,並存值」BEA」。
2.虛擬機在棧中分配給str一個內存,內存中存的是1中的地址。(1指第一步)
3.虛擬機複製一份str,咱們叫str’,str和str’內存不一樣,但存的值都是1的地址。
4.將str’傳入方法體
5.方法體在堆中開闢一塊內存,並存值」BEE」。
6.方法體在堆中再次開闢一塊內存,並存值」bee」。
7.方法體將str’的值改變,存入5的內存地址。
8.方法結束,方法外打印str,因爲str存的是1的地址,全部打印結果是」BEA」。
String的底層是一個不可變數據,因此每次給他賦新的值的時候都至關於新建了一個String對象(若是String常量池裏沒有該字符串的話),咱們能夠驗證一下。
public class Demo{ public static void main(String[] args) { Demo d = new Demo(); //經過比較str的hashCode來比較兩個對象是否爲同一對象 String str = "BEA"; System.out.println("第一次String的hashCode:"+str.hashCode()); str = "bee"; System.out.println("第二次String的hashCode:"+str.hashCode()); //StringBuilder來試一次 StringBuilder s = new StringBuilder("BEA"); System.out.println("第一次StringBuilder的hashCode:"+s.hashCode()); s.append('T'); System.out.println("第二次StringBuilder的hashCode:"+s.hashCode()); System.out.println("調用方法前的StringBuilder對象的值:"+s); d.change(s); System.out.println("調用方法後的StringBuilder對象的值:"+s); } void change(StringBuilder s){ s = s.append('S'); } }
看看執行的結果~對象
tips: hashcode並不能判斷是否爲同一個對象,可是hashcode不一樣的話確定不是同一個對象,hashcode相同的不必定是同一個對象。blog
關注我不會讓你失望喲~