String 字符串常量
StringBuffer字符串變量(線程安全)
StringBuilder字符串變量(非線程安全)
簡要的說, String 類型和StringBuffer類型的主要性能區別其實在於 String 是不可變的對象, 所以在每次對 String 類型進行改變的時候其實都等同於生成了一個新的 String 對象,而後將指針指向新的 String 對象,因此常常改變內容的字符串最好不要用 String ,由於每次生成對象都會對系統性能產生影響,特別當內存中無引用對象多了之後, JVM 的 GC 就會開始工做,那速度是必定會至關慢的。
而若是是使用StringBuffer類則結果就不同了,每次結果都會對StringBuffer對象自己進行操做,而不是生成新的對象,再改變對象引用。因此在通常狀況下咱們推薦使用StringBuffer,特別是字符串對象常常改變的狀況下。而在某些特別狀況下, String 對象的字符串拼接實際上是被 JVM 解釋成了StringBuffer對象的拼接,因此這些時候 String 對象的速度並不會比StringBuffer對象慢,而特別是如下的字符串對象生成中, String 效率是遠要比StringBuffer快的:
String S1 = 「This is only a」 + 「 simple」 + 「 test」;
StringBuffer Sb = new StringBuilder(「This is only a」).append(「 simple」).append(「 test」);
你會很驚訝的發現,生成 String S1 對象的速度簡直太快了,而這個時候StringBuffer竟然速度上根本一點都不佔優點。其實這是 JVM 的一個把戲,在 JVM 眼裏,這個
String S1 = 「This is only a」 + 「 simple」 + 「test」; 其實就是:
String S1 = 「This is only a simple test」; 因此固然不須要太多的時間了。但你們這裏要注意的是,若是你的字符串是來自另外的 String 對象的話,速度就沒那麼快了,譬如:
String S2 = 「This is only a」;
String S3 = 「 simple」;
String S4 = 「 test」;
String S1 = S2 +S3 + S4;
這時候 JVM 會規規矩矩的按照原來的方式去作html
在大部分狀況下StringBuffer > String
StringBufferjava
Java.lang.StringBuffer線程安全的可變字符序列。一個相似於 String 的字符串緩衝區,但不能修改。雖然在任意時間點上它都包含某種特定的字符序列,但經過某些方法調用能夠改變該序列的長度和內容。程序員
每一個字符串緩衝區都有必定的容量。只要字符串緩衝區所包含的字符序列的長度沒有超出此容量,就無需分配新的內部緩衝區數組。若是內部緩衝區溢出,則此容量自動增大。從 JDK 5.0 開始,爲該類增添了一個單個線程使用的等價類,即 StringBuilder 。與該類相比,一般應該優先使用 StringBuilder 類,由於它支持全部相同的操做,但因爲它不執行同步,因此速度更快。編程
可將字符串緩衝區安全地用於多個線程。能夠在必要時對這些方法進行同步,所以任意特定實例上的全部操做就好像是以串行順序發生的,該順序與所涉及的每一個線程進行的方法調用順序一致。
StringBuffer上的主要操做是 append 和 insert 方法,可重載這些方法,以接受任意類型的數據。每一個方法都能有效地將給定的數據轉換成字符串,而後將該字符串的字符追加或插入到字符串緩衝區中。append 方法始終將這些字符添加到緩衝區的末端;而 insert 方法則在指定的點添加字符。
例如,若是 z 引用一個當前內容是「start」的字符串緩衝區對象,則此方法調用 z.append("le") 會使字符串緩衝區包含「startle」,而 z.insert(4, "le") 將更改字符串緩衝區,使之包含「starlet」。
在大部分狀況下StringBuilder > StringBuffer數組
java.lang.StringBuilder緩存
java.lang.StringBuilder一個可變的字符序列是5.0新增的。此類提供一個與StringBuffer兼容的 API,但不保證同步。該類被設計用做StringBuffer的一個簡易替換,用在字符串緩衝區被單個線程使用的時候(這種狀況很廣泛)。若是可能,建議優先採用該類,由於在大多數實現中,它比StringBuffer要快。二者的方法基本相同。安全
可是若是將 StringBuilder 的實例用於多個線程是不安全的。須要這樣的同步,則建議使用 StringBuffer 。性能優化
ok,Talk is cheap,show you the code:app
package com.test; public class Testssb { /** Creates a new instance of testssb */ final static int ttime = 70000;// 測試循環次數 public Testssb() { } public void test(String s){ long begin = System.currentTimeMillis(); for(int i=0;i<ttime;i++){ s += "add"; } long over = System.currentTimeMillis(); System.out.println(" 操做 "+s.getClass().getName()+" 類型使用的時間爲: " + (over - begin) + " 毫秒 " ); } public void test(StringBuffer s){ long begin = System.currentTimeMillis(); for(int i=0;i<ttime;i++){ s.append("add"); } long over = System.currentTimeMillis(); System.out.println(" 操做 "+s.getClass().getName()+" 類型使用的時間爲: " + (over - begin) + " 毫秒 " ); } public void test(StringBuilder s){ long begin = System.currentTimeMillis(); for(int i=0;i<ttime;i++){ s.append("add"); } long over = System.currentTimeMillis(); System.out.println(" 操做 "+s.getClass().getName()+" 類型使用的時間爲: " + (over - begin) + " 毫秒 " ); } // 對 String 直接進行字符串拼接的測試 public void test2(){ String s2 = "abadf"; long begin = System.currentTimeMillis(); for(int i=0;i<ttime;i++){ String s = s2 + s2 + s2 ; } long over = System.currentTimeMillis(); System.out.println(" 操做字符串對象引用相加類型使用的時間爲: " + (over - begin) + " 毫秒 " ); } public void test3(){ long begin = System.currentTimeMillis(); for(int i=0;i<ttime;i++){ String s = "abadf" + "abadf" + "abadf" ; } long over = System.currentTimeMillis(); System.out.println(" 操做字符串相加使用的時間爲: " + (over - begin) + " 毫秒 " ); } public static void main(String[] args){ String s1 ="abc"; StringBuffer sb1 = new StringBuffer("abc"); StringBuilder sb2 = new StringBuilder("abc"); Testssb t = new Testssb(); t.test(s1); t.test(sb1); t.test(sb2); t.test2(); t.test3(); } }
測試結果:jvm
50000 操做 java.lang.String 類型使用的時間爲: 10456 毫秒 操做 java.lang.StringBuffer 類型使用的時間爲: 4 毫秒 操做 java.lang.StringBuilder 類型使用的時間爲: 3 毫秒 操做字符串對象引用相加類型使用的時間爲: 23 毫秒 操做字符串相加使用的時間爲: 1 毫秒 60000 操做 java.lang.String 類型使用的時間爲: 17455 毫秒 操做 java.lang.StringBuffer 類型使用的時間爲: 4 毫秒 操做 java.lang.StringBuilder 類型使用的時間爲: 3 毫秒 操做字符串對象引用相加類型使用的時間爲: 13 毫秒 操做字符串相加使用的時間爲: 1 毫秒 70000 操做 java.lang.String 類型使用的時間爲: 25882 毫秒 操做 java.lang.StringBuffer 類型使用的時間爲: 5 毫秒 操做 java.lang.StringBuilder 類型使用的時間爲: 3 毫秒 操做字符串對象引用相加類型使用的時間爲: 14 毫秒 操做字符串相加使用的時間爲: 1 毫秒
你還能夠往下繼續加大測試數據,只是個人本子扛不住了。。。就不測了。。。
其實我這裏測試並非很公平,由於都放在了一塊兒以前後順序進行,測試方法中間沒有考慮到JVM的GC收集前面產生的無引用對象垃圾而對執行過程的中斷時間。若是你們有更好的想法或者思路歡迎跟我討論:decli AT qq DOT com
關於上文所述:在 JVM 眼裏,這個
String S1 = 「This is only a」 + 「 simple」 + 「test」; 其實就是:
String S1 = 「This is only a simple test」;
這裏的表述不恰當
String S1 = 「This is only a」 + 「 simple」 + 「test」; 其實就是:
String S1 = 「This is only a simple test」; 這麼說是對的,可是若是是jvm的把戲,那麼String S1 = 「This is only a」 + 「 simple」 + 「test」; 是否是會和
String S3=「This is only a」;
S3+=「 simple」;
S3+=「test」;
System.out.println(S1==S3);的結果是同樣呢?很顯然結果是false;
其實這並非JVM的把戲,而是java編譯器的把戲。
給出以下源代碼
package com.test; public class T { public static void main(String[] args) { String s1 = "This is only a" + " simple " + "test"; String s2 = "This is only a"; s2+= " simple "; s2+= "test"; System.out.println(s1==s2); } }
那麼它編譯(JDK5.0)後的class會是什麼樣的呢:
H:\tmp_download>jad -p T.class // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) // Source File Name: T.java package com.test; import java.io.PrintStream; public class T { public T() { } public static void main(String args[]) { String s1 = "This is only a simple test"; String s2 = "This is only a"; s2 = (new StringBuilder(String.valueOf(s2))).append(" simple ").toString(); s2 = (new StringBuilder(String.valueOf(s2))).append("test").toString(); System.out.println(s1 == s2); } } H:\tmp_download>
能夠看到 + 變成了 StringBuilder 的方式,這是Javac的問題,也是Javac優化class的一個技巧。
http://stackoverflow.com/questions/8725739/stringbuilder-usage
注:不要用 Java decompiler 去編譯,這個工具會作一些美化,而要用 JAD:
JAVA高級:反編譯工具jad的簡單用法
http://tech.sina.com.cn/s/2007-09-28/09061768599.shtml
http://www.varaneckas.com/jad/
最後,咱們再來看個常見的問題:
new String() vs literal string(字面量)
Java運行環境有一個字符串常量池(String constant pool),由String類維護。執行語句String s="abc"時,首先查看字符串池中是否存在字符串"abc",若是存在則直接將"abc"賦給s,若是不存在則先在字符串池中新建一個字符串"abc",而後再將其賦給s。
執行語句String s=new String("abc")時,在運行時涉及 2 個String實例,一個是字符串字面量"xyz"所對應的、駐留(intern)在一個全局共享的字符串常量池中的實例,另外一個是經過new String(String)建立並初始化的、內容與"xyz"相同的實例。
前一語句的效率高,後一語句的效率低,由於新建字符串須要佔用內存空間和時間。
注意如下區別:
Also, a little test you can do to drive the point home: String a = new String("xpto"); String b = new String("xpto"); String c = "xpto"; String d = "xpto"; String e = a.intern(); System.out.println(a == b); System.out.println(a == c); System.out.println(c == d); System.out.println(c == e); With all this, you can probably figure out the results of these Sysouts: false false true true Since c and d are the same object, the == comparison holds true.
intern()
對於上面使用new建立的字符串對象,若是想將這個對象的引用加入到字符串常量池,可使用intern方法。
調用intern後,首先檢查字符串常量池中是否有該對象的引用,若是存在,則將這個引用返回給變量,不然將引用加入並返回給變量。
http://stackoverflow.com/questions/14757978/new-string-vs-literal-string-performance
http://rednaxelafx.iteye.com/blog/774673
http://zhidao.baidu.com/question/26614057.html
http://developer.51cto.com/art/201106/266454.htm Java中的String與常量池
http://www.iteye.com/topic/634530 Java堆.棧和常量池 筆記
http://droidyue.com/blog/2014/12/21/string-literal-pool-in-java/?comefrom=http://blogread.cn/news/ Java中的字符串常量池
REF:
是 String , StringBuffer 仍是 StringBuilder ?
http://www.blogjava.net/chenpengyi/archive/2006/05/04/44492.html
http://baike.baidu.com/view/3645996.htm
深刻理解Java中的String
http://g21121.iteye.com/blog/1873262
爲何String類是不可變的?
http://www.importnew.com/7440.html
JDK6 和 JDK7 中的 substring() 實現差別(JDK6和JDK7中的substring()方法)
http://www.importnew.com/7418.html
Java中的String對象是不可變的嗎
http://www.importnew.com/9468.html
Oracle優化Java字符串內部表示
http://www.infoq.com/cn/news/2013/12/Oracle-Tunes-Java-String
如何寫一個不可變類?
http://www.importnew.com/7535.html
深刻理解Java中的final關鍵字
http://www.importnew.com/7553.html
Java 性能優化之 String 篇
http://www.ibm.com/developerworks/cn/java/j-lo-optmizestring/
Integer.valueOf(String) 方法之惑
http://www.importnew.com/9162.html
public static void main(String[] args) throws IOException { System.out.println(Integer.valueOf(127)==Integer.valueOf(127)); System.out.println(Integer.valueOf("127")==Integer.valueOf("127")); System.out.println(Integer.valueOf("128")==Integer.valueOf("128")); System.out.println(Integer.parseInt("128")==Integer.valueOf("128")); } // true false false true
Java中關於String類型的10個問題
http://www.importnew.com/12845.html
什麼是字符串常量池?
http://www.importnew.com/10756.html
Java程序員們最常犯的10個錯誤
http://www.importnew.com/12074.html
Java編程提升性能時需注意的地方
http://blog.jobbole.com/16474/
儘可能使用基本數據類型代替對象:
String str = "hello";
上面這種方式會建立一個「hello」字符串,並且JVM的字符緩存池還會緩存這個字符串;
String str = new String("hello");
此時程序除建立字符串外,str所引用的String對象底層還包含一個char[]數組,這個char[]數組依次存放了h,e,l,l,o
Java中的substring真的會引發內存泄露麼?
http://droidyue.com/blog/2014/12/14/substring-memory-issue-in-java/
淺談StringBuilder
「for循環中使用 "+" 拼接爲何這麼慢」:用 "+" 進行拼接,都會轉化成 StringBuilder 對象,可是在for循環中,每循環一次,就建立一個StringBuilder 對象,若是循環1千萬次,就建立了1千萬個StringBuilder 對象,性能會差不少。