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