天天看點

java中的String,new String,StringBuffer

java.lang.String類對大家來說最熟悉不過了,我們寫java程式很少能不用String的。本文講述如何正确的使用String,内容主要涉及初始化、串聯和比較等操作。

    首先我們必須清楚的一點是String類是final類型的,是以你不可以繼承這個類、不能修改這個類。我們使用String的時候非常簡單,通常都是 String s = "hello",但是Java API中同時提供了一個構造函數為String(String s),是以你也可以這樣使用String s = new String("hello",對于後面這樣初始化一個String的方式是不推薦的,因為new操作符意味着将會在heap上生成一個新的對象,如果這樣的操作發生在一個循環中,那麼代價是慘重的。比如

for(int i = 0;i<1000;i++)

{

    String s = new String("hello";

}

這将會建立1000個String類型的對象,由于String類是final的,是以這樣的操作事實上是每次都生成了一個新的String對象的。如果你使用String s = "hello";那麼就可以實作複用了。

   看這兩條語句:

   String   password="ok"; 

   String   password=new   String("ok";  

   不同的是,第一條現在記憶體中建立了"ok"這個String,然後将reference賦給password,如果這個時候還有一條語句String pwd = "ok";那麼JVM将不再建立"ok",而是直接将第一個"ok"的reference賦給pwd,也就是說,password和pwd是使用同一塊記憶體,而如果加上String   pwd   =   new   String("ok";那JVM将在記憶體中再建立一塊區域放上“ok”這個字元串。

    通常對String的比較有兩種情況,一個是使用==,另一個是使用equals()方法,注意==是對對象的位址進行比較的,而String中的 equals()方法是覆寫了Object類的方法,并且實作為對String對象的内容的比較。是以String s1 = new String("hello";String s2 = new String("hello",我們對s1和s2進行上述比較的時候,前者應該傳回false,因為使用new生成的是兩個不同的對象。後者應該傳回 true因為他們的内容是一樣的,都是"hello"。那麼如果我們還有一個String s3 = "hello";他和s1的比較應該是什麼樣子的呢,答案是s1==s3為false,equals的比較位true。事實上String類是維持着一個 String池的,這個池初始化為空的,當我們String x = "hello"的時候,hello就會被放入這個池中,當我們再次String y = "hello"的時候,他首先去檢查池中是否存在一個和hello内容一樣的對象,如果存在的話就會把這個引用傳回給y,如果不存在的話,就會建立一個并放入到池中。這樣就實作了複用。在String有一個方法intern()他可以把String的對象放入到池沖并傳回池中的對象。如果我們對s1 (String s1 = new String("hello")調用intern,s1 = s1.intern()這時候,我們再把s1和s3進行“==”的判斷,你會發現結果傳回true!

   String是一個final Class,StringBuffer不是。是以對于 String a = "yacht" ,String b = "yacht1" String c = a + b ; 存在一個對象拷貝構造和解析的消耗問題;對于一個StringBuffer來說,StringBuffer sb = new StringBuffer();sb.append("yacht" ; sb.append("yacht1"; 因為StringBuffer是一個可以執行個體化的類,而且它的内建機制是維護了一個capacity大小的字元數組,是以它的append操作不存在對象的消耗問題,是以我覺得如果存在String連接配接這種事情,StringBuffer來做會好很多。

    但事情并不是這麼簡單,看下面代碼

    String a = "yacht1" + "yacht2" + "yacht3" + "yacht4";

    StringBuffer sb = new StringBuffer();

    sb.append("yacht1";

    sb.append("yacht2";

    sb.append("yacht3";

    sb.append("yacht4";

    String a = sb.toString();

    如果按照我先前說的看法,第一個效率肯定比第二個低,但經過測試不是這樣,為什麼?這裡,我們需要了解程式過程的兩個時期,一個是編譯時,一個是運作時,在編譯時,編譯器會對你的程式做出優化,是以第一個的String a會被優化成yacht1yacht2yacht3yacht4,而第二個的StringBuffer隻會在運作時才處理。是以效率是不一樣的。

    如果代碼是這樣的:

    String a ;

    for(int i = 0; i< 100000;i++)

      a += String.valueOf(i);

     StringBuffer sb = new StringBuffer();

    for(int i = 0; i< 100000;i++)

     sb.append(i) ;

    String a = sb.toString();

    如果是這種情況的話,第一個的效率就大大不如藍色,差別在哪裡,就在于運作時和編譯時的優化問題上!

效率:String 與 StringBuffer

    情景1:

    (1) String result = "hello" + " world";

    (2) StringBuffer result = new String().append("hello".append(" world";

        (1) 的效率好于 (2),不要奇怪,這是因為JVM會做如下處理

        編譯前   String result = "hello" + " world";

        編譯後   String result = "hello world";

    情景2:

    (1) public String getString(String s1, String s2) {

            return s1 + s2;

        }

    (2) public String getString(String s1, String s2) {

            return new StringBuffer().append(s1).append(s2);

        }

        (1) 的效率與 (2) 一樣,這是因為JVM會做如下處理

        編譯前   return s1 + s2;

        編譯後   return new StringBuffer().append(s1).append(s2);

    情景3:

    (1) String s = "s1";

              s += "s2";

              s += "s3";

    (2) StringBuffer s = new StringBuffer().append("s1".append("s2".append("s3";

        (2) 的效率好于(1),因為String是不可變對象,每次"+="操作都會造成構造新的String對象

    情景4:

    (1) StringBuffer s = new StringBuffer();

        for (int i = 0; i < 50000; i ++) {

            s.append("hello";

        }

    (2) StringBuffer s = new StringBuffer(250000);

        for (int i = 0; i < 50000; i ++) {

            s.append("hello";

        }  

        (2) 的效率好于 (1),因為StringBuffer内部實作是char數組,預設初始化長度為16,每當字元串長度大于char

        數組長度的時候,JVM會構造更大的新數組,并将原先的數組内容複制到新數組,(2)避免了複制數組的開銷