String中的坑
最近看到一道關於String的面試題,差點讓我以爲String是值傳遞,就是下面這個例子,體驗下:<!-- more -->
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是基本類型?
其實都不是,後來通過查閱相應資料發現,jvm在實例化字符串時會使用字符串常量池,把str作爲參數傳入change()方法。jvm複製了一份str變量,爲了便於理解我們叫它str'。這個時候str和str'都指向字符串常量池中的“abc”。
當我們執行s = s.replace('A', 'E');
其實相當於執行了s = new String(s.replace('A', 'E'));
要理解上面這兩段話,就要從java的底層結構說起了。java的內存模型大體分爲 堆 和 棧 (細分還有方法區,和程序計數器等)。
1.基本類型的變量放在棧裏;
2.封裝類型中,對象放在堆裏,對象的引用放在棧裏。
<font color=red>java在方法傳遞參數時,是將變量複製一份,然後傳入方法體去執行。</font>
根據這些再細分一下jvm的執行過程
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相同的不一定是同一個對象。
關注我不會讓你失望喲~