1. 程式人生 > >對String型別的認識以及編譯器優化

對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

,比如s==w?當然不等,s和w指向堆記憶體中不同的String物件。如果判斷兩個String物件相等呢?用equals方法。

有如下一段程式碼,請選擇其執行結果()

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物件,計算的結果在堆記憶體中。