String 做爲最基礎的引用數據類型,平常的開發中被大量的使用。基於不可變的特性,一旦被過分地使用,堆內存就會負荷不堪,甚至影響性能,爲此,Java 設計者特地爲 String 在方法區中開闢了字符串常量池,以減小 String 的實例建立,然而,在面對大數量的狀況下,字符串常量池也未必能解決問題,所以,AbstractStringBuilder 應運而生,就是爲了解決 String頻繁建立而引起的內存性能降低的問題。java
帶着兩個問題,去看看String / StringBuffer / StringBuilder 的區別程序員
String vs AbstractStringBuilder編程
StringBuffer vs StringBuilder安全
String / StringBuffer / StringBuilder 的使用策略多線程
擴容機制app
Stringide
不可變性:從新建立一個對象模塊化
String 底層代碼實現:性能
String 類被 final 修飾,該類不能被繼承測試
value[] 屬性 被final 修飾 ,引用不能修改
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; // /** Cache the hash code for the string */ private int hash; // Default to 0``` //other codes
測試代碼:
String str = new String("a"); str = str + 「b」 ;
圖示:
AbstractStringBuilder
可變性
AbstractStringBuilder 底層代碼實現:
value[] 相對於 String ,沒有被final修飾
append("String") 返回時對象自己,不會建立新的對象
abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ char[] value; /** * The count is the number of characters used. */ int count; // other codes public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; } //other codes }
測試代碼:
StringBuffer sb = new StringBuffer("a"); sb.append("b");
圖示:
性能比較
public class StringBufferWithStringBuilder { public void testString() { long start = System.currentTimeMillis(); String str = null; for (int i = 0; i < 20000; i++) { str = str + i + ","; } System.out.println(System.currentTimeMillis() - start); } public void testStringBuffer() { long start = System.currentTimeMillis(); StringBuffer sbuf = new StringBuffer(); for (int i = 0; i < 20000; i++) { sbuf.append(i + ","); } System.out.println(System.currentTimeMillis() - start); } public void testStringBulider() { long start = System.currentTimeMillis(); StringBuilder builder = new StringBuilder(); for (int i = 0; i < 20000; i++) { builder.append(i + ","); } System.out.println(System.currentTimeMillis() - start); } @Test public void test(){ testString(); testStringBuffer(); testStringBulider(); } }
經過測試數據得知,在性能和效率上:StringBuilder>StringBuffer>String
緣由在於:
String 每執行一次 + 重載運算符,必須建立一個新的對象
StringBuilder 與 StringBuffer相比,少了同步鎖
線程安全
StringBuffer 是線程安全的
StringBuilder 是線程不安全
底層實現: StringBuffer 經過 synchronized 關鍵字的修飾,保證了資源不會被搶佔,從而確保了線程安全
/** * @since 1.5 */ @Override public synchronized void trimToSize() { super.trimToSize(); } /** * @throws IndexOutOfBoundsException {@inheritDoc} * @see #length() */ @Override public synchronized void setLength(int newLength) { toStringCache = null; super.setLength(newLength); }
基本原則:若是要操做少許的數據,用String ;單線程操做大量數據,用StringBuilder ;多線程操做大量數據,用StringBuffer
不要使用String類的"+"來進行頻繁的拼接,由於那樣的性能極差的,應該使用StringBuffer或StringBuilder類,這在Java的優化上是一條比較重要的原則
爲了得到更好的性能,在構造 StringBuffer 或 StringBuilder 時應儘量指定它們的容量。固然,若是你操做的字符串長度(length)不超過 16 個字符就不用了,當不指定容量(capacity)時默認構造一個容量爲16的對象。不指定容量會顯著下降性能
StringBuilder通常使用在方法內部來完成相似"+"功能,由於是線程不安全的,因此用完之後能夠丟棄。StringBuffer主要用在全局變量中
相同狀況下使用 StringBuilder 相比使用 StringBuffer 僅能得到 10%~15% 左右的性能提高,但卻要冒多線程不安全的風險。而在現實的模塊化編程中,負責某一模塊的程序員不必定能清晰地判斷該模塊是否會放入多線程的環境中運行,所以:除非肯定系統的瓶頸是在 StringBuffer 上,而且肯定你的模塊不會運行在多線程模式下,才能夠採用StringBuilder;不然仍是用StringBuffer