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