對String型別的認識以及編譯器優化
Java中String不是基本型別,但是有些時候和基本型別差不多,如String b = "tao" ; 可以對變數直接賦值,而不用 new 一個物件(當然也可以用 new)。
Java中的變數和基本型別的值存放於棧記憶體,而new出來的物件本身存放於堆記憶體,指向物件的引用還是存放在棧記憶體。例如如下的程式碼:
int i=1;
String s = new String( "Hello World" );
變數i和s以及1存放在棧記憶體,而s指向的物件”Hello World”存放於堆記憶體。
棧記憶體的一個特點是資料共享,這樣設計是為了減小記憶體消耗,前面定義了i=1,i和1都在棧記憶體內,如果再定義一個j=1,此時將j放入棧記憶體,然後查詢棧記憶體中是否有1,如果有則j指向1。如果再給j賦值2,則在棧記憶體中查詢是否有2,如果沒有就在棧記憶體中放一個2,然後j指向2。
如果j++,這時指向的變數並不會改變,而是在棧內尋找新的常量(比原來的常量大1),如果棧記憶體有則指向它,如果沒有就在棧記憶體中加入此常量並將j指向它。
這種基本型別之間比較大小和我們邏輯上判斷大小是一致的。
如定義i和j是都賦值1,則i==j結果為true。==用於判斷兩個變數指向的地址是否一樣。i==j就是判斷i指向的1和j指向的1是同一個嗎?當然是了。對於直接賦值的字串常量(如String s=“Hello World”;中的Hello World)也是存放在棧記憶體中,而new出來的字串物件(即String物件)是存放在堆記憶體中。如果定義String s=“Hello World”和String w=“Hello World”,s==w嗎?肯定是true,因為他們指向的是同一個Hello World。
堆記憶體沒有資料共享的特點,前面定義的String s = new String( "Hello World" );後,變數s在棧記憶體內,Hello World 這個String物件在堆記憶體內。如果定義String w = new String( "Hello World" );,則會在堆記憶體建立一個新的String物件,變數w存放在棧記憶體,w指向這個新的String物件。堆記憶體中不同物件(指同一型別的不同物件)的比較如果用==則結果肯定都是false
有如下一段程式碼,請選擇其執行結果()
public class StringDemo{ private static final String MESSAGE="taobao"; public static void main(String [] args) { String a ="tao"+"bao"; String b="tao"; String c="bao"; System.out.println(a==MESSAGE); System.out.println((b+c)==MESSAGE); } }答案:C 解析:
MESSAGE 成員變數及其指向的字串常量肯定都是在棧記憶體裡的,變數 a 運算完也是指向一個字串“ taobao ”啊?是不是同一個呢?這涉及到編譯器優化問題。對於字串常量的相加,在編譯時直接將字串合併,而不是等到執行時再合併。也就是說
String a = "tao" + "bao" ;和String a = "taobao" ;編譯出的位元組碼是一樣的。所以等到執行時,根據上面說的棧記憶體是資料共享原則,a和MESSAGE指向的是同一個字串。而對於後面的(b+c)又是什麼情況呢?b+c只能等到執行時才能判定是什麼字串,編譯器不會優化。執行時b+c計算出來的"taobao"和棧記憶體裡已經有的"taobao"是一個嗎?不是。b+c計算出來的"taobao"應該是放在堆記憶體中的String物件。這可以通過System. out .println( (b+c)== MESSAGE );的結果為false來證明這一點。Java對String的相加是通過StringBuffer實現的,先構造一個StringBuffer裡面存放”tao”,然後呼叫append()方法追加”bao”,然後將值為”taobao”的StringBuffer轉化成String物件。StringBuffer物件在堆記憶體中,那轉換成的String物件理所應當的也是在堆記憶體中。
下面改造一下這個語句System. out .println( (b+c).intern()== MESSAGE );結果是true, intern() 方法會先檢查 String 池 ( 或者說成棧記憶體 ) 中是否存在相同的字串常量,如果有就返回。所以 intern()返回的就是MESSAGE指向的"taobao"。
再把變數b和c的定義改一下,
final String b = "tao" ;
final String c = "bao" ;
System. out .println( (b+c)== MESSAGE );
現在b和c不可能再次賦值了,所以編譯器將b+c編譯成了”taobao”。因此,這時的結果是true。
在字串相加中,只要有一個是非final型別的變數,編譯器就不會優化,因為這樣的變數可能發生改變,所以編譯器不可能將這樣的變數替換成常量。例如將變數b的final去掉,結果又變成了false。這也就意味著會用到StringBuffer物件,計算的結果在堆記憶體中。