深入Java核心 Java内存分配原理精讲(3)
String常量池問題的幾個例子
下面是幾個常見例子的比較分析和理解:
String?a?=?"a1";? ?
String?b?=?"a"?+?1;? ?
System.out.println((a?==?b));?//result?=?true??
String?a?=?"atrue";? ?
String?b?=?"a"?+?"true";? ?
System.out.println((a?==?b));?//result?=?true??
String?a?=?"a3.4";? ?
String?b?=?"a"?+?3.4;? ?
System.out.println((a?==?b));?//result?=?true?
分析:JVM對于字符串常量的"+"號連接,將程序編譯期,JVM就將常量字符串的"+"連接優(yōu)化為連接后的值,拿"a" + 1來說,經(jīng)編譯器優(yōu)化后在class中就已經(jīng)是a1。在編譯期其字符串常量的值就確定下來,故上面程序最終的結(jié)果都為true。
String?a?=?"ab";? ?
String?bb?=?"b";? ?
String?b?=?"a"?+?bb;? ?
System.out.println((a?==?b));?//result?=?false?
分析:JVM對于字符串引用,由于在字符串的"+"連接中,有字符串引用存在,而引用的值在程序編譯期是無法確定的,即"a" + bb無法被編譯器優(yōu)化,只有在程序運(yùn)行期來動態(tài)分配并將連接后的新地址賦給b。所以上面程序的結(jié)果也就為false。
String?a?=?"ab";? ?
final?String?bb?=?"b";? ?
String?b?=?"a"?+?bb;? ?
System.out.println((a?==?b));?//result?=?true?
分析:和[3]中唯一不同的是bb字符串加了final修飾,對于final修飾的變量,它在編譯時被解析為常量值的一個本地拷貝存儲到自己的常量 池中或嵌入到它的字節(jié)碼流中。所以此時的"a" + bb和"a" + "b"效果是一樣的。故上面程序的結(jié)果為true。
String?a?=?"ab";? ?
final?String?bb?=?getBB();? ?
String?b?=?"a"?+?bb;? ?
System.out.println((a?==?b));?//result?=?false???
private?static?String?getBB()?{ ?
return?"b";? ?
}?
分析:JVM對于字符串引用bb,它的值在編譯期無法確定,只有在程序運(yùn)行期調(diào)用方法后,將方法的返回值和"a"來動態(tài)連接并分配地址為b,故上面 程序的結(jié)果為false。
通過上面4個例子可以得出得知:
String? s? =? "a" + "b" + "c";?
??
就等價于String s = "abc";??
String? a? =? "a";???
String? b? =? "b";???
String? c? =? "c";???
String? s? =?? a? +? b? +? c;?
這個就不一樣了,最終結(jié)果等于:?
?
StringBuffer?temp?=?new?StringBuffer();??? ?
temp.append(a).append(b).append(c);??? ?
String?s?=?temp.toString();?
由上面的分析結(jié)果,可就不難推斷出String 采用連接運(yùn)算符(+)效率低下原因分析,形如這樣的代碼:
public?class?Test?{ ?
public?static?void?main(String?args[])?{ ?
String?s?=?null; ?
for(int?i?=?0;?i?<?100;?i++)?{ ?
s?+=?"a"; ?
} ?
} ?
}?
每做一次 + 就產(chǎn)生個StringBuilder對象,然后append后就扔掉。下次循環(huán)再到達(dá)時重新產(chǎn)生個StringBuilder對象,然后 append 字符串,如此循環(huán)直至結(jié)束。如果我們直接采用 StringBuilder 對象進(jìn)行 append 的話,我們可以節(jié)省 N - 1 次創(chuàng)建和銷毀對象的時間。所以對于在循環(huán)中要進(jìn)行字符串連接的應(yīng)用,一般都是用StringBuffer或StringBulider對象來進(jìn)行 append操作。
String對象的intern方法理解和分析:
public?class?Test4?{ ?
private?static?String?a?=?"ab";? ?
public?static?void?main(String[]?args){ ?
String?s1?=?"a"; ?
String?s2?=?"b"; ?
String?s?=?s1?+?s2; ?
System.out.println(s?==?a);//false??
System.out.println(s.intern()?==?a);//true????
} ?
}?
這里用到Java里面是一個常量池的問題。對于s1+s2操作,其實是在堆里面重新創(chuàng)建了一個新的對象,s保存的是這個新對象在堆空間的的內(nèi)容,所 以s與a的值是不相等的。而當(dāng)調(diào)用s.intern()方法,卻可以返回s在常量池中的地址值,因為a的值存儲在常量池中,故s.intern和a的值相等。
總結(jié)
棧中用來存放一些原始數(shù)據(jù)類型的局部變量數(shù)據(jù)和對象的引用(String,數(shù)組.對象等等)但不存放對象內(nèi)容
堆中存放使用new關(guān)鍵字創(chuàng)建的對象.
字符串是一個特殊包裝類,其引用是存放在棧里的,而對象內(nèi)容必須根據(jù)創(chuàng)建方式不同定(常量池和堆).有的是編譯期就已經(jīng)創(chuàng)建好,存放在字符串常 量池中,而有的是運(yùn)行時才被創(chuàng)建.使用new關(guān)鍵字,存放在堆中。
轉(zhuǎn)載于:https://blog.51cto.com/wuqinglong/1431774
總結(jié)
以上是生活随笔為你收集整理的深入Java核心 Java内存分配原理精讲(3)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 来势汹汹!曝OPPO三款新机三证备案完成
- 下一篇: Java线程:创建与启动