String类型的认识以及编译器优化
Java中String不是基本類型,但是有些時候和基本類型差不多,如String b =?"tao"?;?可以對變量直接賦值,而不用?new?一個對象(當(dāng)然也可以用?new)。所以String這個類型值得好好研究下。
Java中的變量和基本類型的值存放于棧內(nèi)存,而new出來的對象本身存放于堆內(nèi)存,指向?qū)ο蟮囊眠€是存放在棧內(nèi)存。例如如下的代碼:
int??i=1;
????String?s?=??new??String(?"Hello World"?);
變量i和s以及1存放在棧內(nèi)存,而s指向的對象”Hello World”存放于堆內(nèi)存。
?
?
?
?
棧內(nèi)存的一個特點是數(shù)據(jù)共享,這樣設(shè)計是為了減小內(nèi)存消耗,前面定義了i=1,i和1都在棧內(nèi)存內(nèi),如果再定義一個j=1,此時將j放入棧內(nèi)存,然后查找棧內(nèi)存中是否有1,如果有則j指向1。如果再給j賦值2,則在棧內(nèi)存中查找是否有2,如果沒有就在棧內(nèi)存中放一個2,然后j指向2。也就是如果常量在棧內(nèi)存中,就將變量指向該常量,如果沒有就在該棧內(nèi)存增加一個該常量,并將變量指向該常量。
?
?
如果j++,這時指向的變量并不會改變,而是在棧內(nèi)尋找新的常量(比原來的常量大1),如果棧內(nèi)存有則指向它,如果沒有就在棧內(nèi)存中加入此常量并將j指向它。這種基本類型之間比較大小和我們邏輯上判斷大小是一致的。如定義i和j是都賦值1,則i==j結(jié)果為true。==用于判斷兩個變量指向的地址是否一樣。i==j就是判斷i指向的1和j指向的1是同一個嗎?當(dāng)然是了。對于直接賦值的字符串常量(如String s=“Hello World”;中的Hello World)也是存放在棧內(nèi)存中,而new出來的字符串對象(即String對象)是存放在堆內(nèi)存中。如果定義String s=“Hello World”和String w=“Hello World”,s==w嗎?肯定是true,因為他們指向的是同一個Hello World。
?
?
堆內(nèi)存沒有數(shù)據(jù)共享的特點,前面定義的String?s?=??new??String(?"Hello World"?);后,變量s在棧內(nèi)存內(nèi),Hello World?這個String對象在堆內(nèi)存內(nèi)。如果定義String w =?new??String(?"Hello World"?);,則會在堆內(nèi)存創(chuàng)建一個新的String對象,變量w存放在棧內(nèi)存,w指向這個新的String對象。堆內(nèi)存中不同對象(指同一類型的不同對象)的比較如果用==則結(jié)果肯定都是false,比如s==w?當(dāng)然不等,s和w指向堆內(nèi)存中不同的String對象。如果判斷兩個String對象相等呢?用equals方法。
?
?
?
說了這么多只是說了這道題的鋪墊知識,還沒進入主題,下面分析這道題。?MESSAGE?成員變量及其指向的字符串常量肯定都是在棧內(nèi)存里的,變量?a?運算完也是指向一個字符串“?taobao?”啊?是不是同一個呢?這涉及到編譯器優(yōu)化問題。對于字符串常量的相加,在編譯時直接將字符串合并,而不是等到運行時再合并。也就是說
String a =??"tao"?+?"bao"?;和String a =??"taobao"?;編譯出的字節(jié)碼是一樣的。所以等到運行時,根據(jù)上面說的棧內(nèi)存是數(shù)據(jù)共享原則,a和MESSAGE指向的是同一個字符串。而對于后面的(b+c)又是什么情況呢?b+c只能等到運行時才能判定是什么字符串,編譯器不會優(yōu)化,想想這也是有道理的,編譯器怕你對b的值改變,所以編譯器不會優(yōu)化。運行時b+c計算出來的"taobao"和棧內(nèi)存里已經(jīng)有的"taobao"是一個嗎?不是。b+c計算出來的"taobao"應(yīng)該是放在堆內(nèi)存中的String對象。這可以通過System.?out?.println( (b+c)==MESSAGE?);的結(jié)果為false來證明這一點。如果計算出來的b+c也是在棧內(nèi)存,那結(jié)果應(yīng)該是true。Java對String的相加是通過StringBuffer實現(xiàn)的,先構(gòu)造一個StringBuffer里面存放”tao”,然后調(diào)用append()方法追加”bao”,然后將值為”taobao”的StringBuffer轉(zhuǎn)化成String對象。StringBuffer對象在堆內(nèi)存中,那轉(zhuǎn)換成的String對象理所應(yīng)當(dāng)?shù)囊彩窃诙褍?nèi)存中。下面改造一下這個語句System.?out.println( (b+c).intern()==?MESSAGE?);結(jié)果是true,?intern()?方法會先檢查?String?池?(?或者說成棧內(nèi)存?)?中是否存在相同的字符串常量,如果有就返回。所以?intern()返回的就是MESSAGE指向的"taobao"。再把變量b和c的定義改一下,
final??String?b =??"tao"?;
?????????final??String?c =??"bao"?;
????????????
???????System.?out?.println( (b+c)==?MESSAGE?);
現(xiàn)在b和c不可能再次賦值了,所以編譯器將b+c編譯成了”taobao”。因此,這時的結(jié)果是true。
在字符串相加中,只要有一個是非final類型的變量,編譯器就不會優(yōu)化,因為這樣的變量可能發(fā)生改變,所以編譯器不可能將這樣的變量替換成常量。例如將變量b的final去掉,結(jié)果又變成了false。這也就意味著會用到StringBuffer對象,計算的結(jié)果在堆內(nèi)存中。
????如果對指向堆內(nèi)存中的對象的String變量調(diào)用intern()會怎么樣呢?實際上這個問題已經(jīng)說過了,(b+c).intern(),b+c的結(jié)果就是在堆內(nèi)存中。對于指向棧內(nèi)存中字符串常量的變量調(diào)用intern()返回的還是它自己,沒有多大意義。它會根據(jù)堆內(nèi)存中對象的值,去查找String池中是否有相同的字符串,如果有就將變量指向這個string池中的變量。
String a = "tao"+"bao";
???????String b = new String("taobao");
?????
??????System.out.println(a==MESSAGE);?//true
??????System.out.println(b==MESSAGE);??//false
?????
??????b = b.intern();
??????System.out.println(b==MESSAGE); //true
System.?out?.println(a==a.intern());??//true
轉(zhuǎn)載于:https://www.cnblogs.com/nxjblog/p/10574550.html
總結(jié)
以上是生活随笔為你收集整理的String类型的认识以及编译器优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Generator函数自执行
- 下一篇: 使用Jmeter 创建Post请求