9.String StringBuffer StringBuilder

String,StringBuffer和StringBuilder三者的講解

  對於StringBuffer和StringBuilder是對Stirng的一個優化。html

  以前已經說過了,String對象一旦建立後,String中的內容是不可以改變的。每次改變String都會建立新的對象。這樣的話會形成相應空間的浪費。java

介於此jdk開 發人員計出了StringBuffer和StringBuilder,對於後者而言它們的內容是可以動態改變的。而StringBuffer和StringBuilder的區別就在於StringBuffer是線程安全的StringBuffer中的絕大部分方法都加了同步關鍵字)。數組

  而StringBuilder是線程不安全的。由於StringBuilder是線程不安全的,因此其運行效率會更高,若是一個字符串 是在方法裏面定義的話,這種狀況僅僅可能有一個線程訪問它,不存在不安全的因素,這時推薦使用StringBuilder。安全

  若是一個實例變量是在類裏面定義的,而且在多線程 下會訪問到,這時最好使用StringBuffer。多線程

擴展:app

1 String類框架

    String類在java的java.lang.String包下面,須要特別說明的是String類是final關鍵字修飾的,也就是說String類是不可以被繼承的,String中的內容一旦被建立後是不能被修改的。Stirng是對象也不是8種基本數據類型post

   1) 具體的講解請看下面的例子:.優化

package com.yonyou.test;
 
 
class Test{
  public static void main(String[] args) {
    String str=new String("龍不吟");
    str=new String("虎不嘯");//原始String對象中str的內容到底變了沒有?
        System.out.println(str);
     
    //下面也是一個String的例子
    String str2="天下太平";
    str2=str2+"國泰民安";//原始String對象中的str2到底變了沒有?
    System.out.println(str2);
      
}
 }

首先說明上述原始String對象中的內容都沒有改變!ui

       對於這個問題你們能夠這樣理解:

       若是你們看過String的源碼能夠發現,String字符串中的內容存儲在char數組中的。

       在判斷原始String對象str和str2的是否改變了,這裏須要明白一個問題,在java中相關對象的引用變量通常都存在棧中,而相關的對象都是存在堆中的,棧中的值指向了它所引用的對象(堆中相應的對象的地址)。

     棧 :由JVM分配區域,用於保存線程執行的動做和數據引用。棧是一個運行的單位,Java中一個線程就會相應有一個線程棧與之對應。

            通常是用於存儲的基本數據類型的局部變量(注意這裏僅僅是局部的,對於全局變量不能這樣定義哦?)

            

     堆 :由JVM分配的,用於存儲對象等數據的區域。通常用於存儲咱們new出來的對象。

 

     常量池 :在堆中分配出來的一塊存儲區域,用於存儲顯式 的String,float或者integer.例如String str="abc"; abc這個字符串是顯式聲明,因此存儲在常量池。

 

對於java內存的更詳細的講解請參考:http://www.cnblogs.com/xiohao/p/4278173.html 這裏再也不累述。

      例如:

      建立一個對象String str=new String("Hello World");

      對於變量str而言,它表明的是引用變量,它的值是存儲在棧中的,而new String("Hello World")會建立一個新的對象,而對象的值是存儲在堆中的。而引用

      變量str指向對中的對象new String("Hello World"); 這樣看來視乎上面的問題就很好解釋了。

       因爲是String修飾的str和str2而言,它們的引用變量的自己是不可以改變,可是它們指向的對象,好比說指向的堆中地址倒是能夠改變的。

       因此說上面String對象str和str2所對應的原始對象都沒有改變,僅僅是str和str2所對應的引用變量的指向發生的改變。這段話有一些繞

       理解起來不是那麼容易,請多讀幾遍,反覆思考一下。

       

      2) 接下來你們能夠來理解一下

  

package com.yonyou.test;
 
 
class Test{
  public static void main(String[] args) {
     String str=new String("Hello World");
     String str2="Hello World";
     System.out.println("str和str2的equals值相同嗎?"+str.equals(str2));
     System.out.println("str和str2的==值相同嗎?"+(str==str2));//注意此處的str==str2必須用括號括起來,
                                                                 // 不然的話 字符鏈接符號 +的優先級高於==,實際上進行的比較是
                                                                 //str和str2的==值相同嗎?Hello World和Hello World是否相同
 }
}

輸出結果爲:

 str和str2的equals值相同嗎?true
 str和str2的==值相同嗎?false

 這些結果右是怎麼輸出來的呢?

 首先咱們須要明白equals和==的區別和聯繫

 對於equals而言,它是 Object類中方法以下:

...

     * @param   obj   the reference object with which to compare.
     * @return  <code>true</code> if this object is the same as the obj
     *          argument; <code>false</code> otherwise.
     * @see     #hashCode()
     * @see     java.util.Hashtable
     */

 public boolean equals(Object obj) {
 return (this == obj);
    }

 經過在這裏查看Object類中equals的方法咱們知道,若是一個類沒有重寫Object類中的equals方法的話,那麼它的做用和==的做用是同樣的。說白了是

   沒有任何區別的。可是若是用戶能夠根據本身的需求進行重寫equals方法那樣的話,equals比較的返回值就和相關的需求相關了。

   如在jdk中的String類中的equals方法是這樣重寫的:

/**
     * Compares this string to the specified object.  The result is {@code
     * true} if and only if the argument is not {@code null} and is a {@code
     * String} object that represents the same sequence of characters as this
     * object.
     *
     * @param  anObject
     *         The object to compare this {@code String} against
     *
     * @return  {@code true} if the given object represents a {@code String}
     *          equivalent to this string, {@code false} otherwise
     *
     * @see  #compareTo(String)
     * @see  #equalsIgnoreCase(String)
     */
    public boolean equals(Object anObject) {
 if (this == anObject) {
     return true;
 }
 if (anObject instanceof String) {
     String anotherString = (String)anObject;
     int n = count;
     if (n == anotherString.count) {
  char v1[] = value;
  char v2[] = anotherString.value;
  int i = offset;
  int j = anotherString.offset;
  while (n-- != 0) {
      if (v1[i++] != v2[j++])
   return false;
  }
  return true;
     }
 }
 return false;
    }

這樣咱們就能夠發現,對於String對象中的equals方法而言,它的目的是比較兩個對象所對應的值是否相同,注意僅僅是兩個對象的值,跟這兩個對象

的引用(地址沒有任何關係)。

    而對於==而言,它在java中的主要做用則是用來比較java中一些基本類型(如int,long,float等)的值是否相同以及比較兩個對象是否有相同(即兩個對象的引用

    地址是否相同)。

    這也就明白了爲何上面的對象equals的值相同而==的值不一樣。

 

      3)對於下面聲明的這個變量建立了幾個對象?

          String str=new Stirng("xyz"); //建立了幾個對象

          String str2="abc";//建立了幾個對象

          首先說「String str=new Stirng("xyz");」建立了一個或者兩個

               對於「xyz」這個對象而言,它是存放在字符串的緩衝區中的,無論出現多少遍,都是緩衝區中的那一個。而new String()每次都會建立一個新的對象。因此若是以前建立

          過「xyz」這個對象的話,那麼久建立一個對象,而若是以前要是沒有建立過這個字符串的話,那麼就會建立兩個對象。

          其次說String str2=「abc」會建立零個或者一個。

              這個是爲何我就不哆嗦了。

           須要注意str和str2是變量名不是對象。

         

          請看下面的這條語句建立了幾個對象?

          String str="a"+"b"+"c"+"d"+"e"+"f"+"g"+"h"+"i"+"j"+"k";

          沒錯這裏僅僅建立了一個對象即str=」abcdefghijk「;

          爲了更好的說明這個問題咱們來看下面的例子:

   

package com.xiaohao.test;
 
public class Test{
    public static void main(String[] args) {
      String str1="ab";
      String str2="a"+"b";
      String str3="b";
      String str4="a"+str3;
      System.out.println("str1和str2相等嗎?"+(str1==str2));
      System.out.println("str1和str4相等嗎?"+(str1==str4));
    }
 
}

 上面程序的輸出結果爲:

       str1和str2相等嗎?true
       str1和str4相等嗎?false

      這說明javac編譯的時候能夠對字符串常量直接相加的表達式進行優化,沒必要等到運行期在進行加法處理,而是在編譯的時候直接去掉加號,直接將其編譯成這些常量

      相連的結果。而對於str4而言因爲str3是變量,不是字符串常量,因此最終的結果爲false。

     

      下面來說StringBuffer和StringBuilder

      對於StringBuffer和StringBuilder是對Stirng的一個優化。

      以前已經說過了,String對象一旦建立後,String中的內容是不可以改變的。每次改變String都會建立新的對象。這樣的話會形成相應空間的浪費。介於此jdk額開

      發人員計出了StringBuffer和StringBuilder,對於後者而言它們的內容是可以動態改變的。而StringBuffer和StringBuilder的區別就在於StringBuffer是線程安全的

    (StringBuffer中的絕大部分方法都加了同步關鍵字)而StringBuilder是線程不安全的。由於StringBuilder是線程不安全的,因此其運行效率會更高,若是一個字符串

      是在方法裏面定義的話,這種狀況僅僅可能有一個線程訪問它,不存在不安全的因素,這時推薦使用StringBuilder。若是一個實例變量是在類裏面定義的,而且在多線程

      下會訪問到,這時最好使用StringBuffer

       爲了更好的理解StringBuffer和StringBuilder的效率問題,請看下面的例子:

package com.xiaohao.test;
 
public class Test2 {
    public static void main(String[] args) {
        String str=new String();
        StringBuffer sb1=new StringBuffer();
        StringBuilder sb2=new StringBuilder();
        long startTime=System.currentTimeMillis();
        for(int i=0;i<100000;i++)
        {
          str=str+i;
        }
        long endTime=System.currentTimeMillis();
        System.out.println("Stirng消耗的時間爲:"+(endTime-startTime));
         
        startTime=System.currentTimeMillis();
        for(int i=0;i<100000;i++)
        {
            sb2.append(i);
        }
        endTime=System.currentTimeMillis();
        System.out.println("StirngBuilder消耗的時間爲:"+(endTime-startTime)); 
         
        startTime=System.currentTimeMillis();
        for(int i=0;i<100000;i++)
        {
           sb1.append(i);
        }
       endTime=System.currentTimeMillis();
       System.out.println("StirngBuffer消耗的時間爲:"+(endTime-startTime)); 
        
 
   }
}

運行結果爲:

    Stirng消耗的時間爲:42185
    StirngBuilder消耗的時間爲:0
    StirngBuffer消耗的時間爲:0

    相關效率,你懂的~~~ 

   另外StringBuffer和StringBuilder沒有重寫equals和hashcode方法,它們在存儲在java集合框架的時候可能出現問題。

參考資料:http://www.cnblogs.com/xiohao/p/4271140.html

能夠用來計時:納秒級別的

long t3 = System.nanoTime()

2018年8月25日 09:10:13

2019年6月26日 09:08:52

相關文章
相關標籤/搜索