1 先概括说下String,StringBuffer,StringBuilder三者的区别:
长度方面
String是定长的。
StringBuffer,StringBuilder是可变长的。
线程安全方面
String,StringBuffer是线程安全的。
StringBuilder是线程不安全的。
性能方面
StringBuffer要好于String。
StringBuilder要好于StringBuffer。
2 源码分析
String
String对象一旦创建之后该对象是不可更改的
* Strings are constant; their values cannot be changed after they * are created. String buffers support mutable strings. * Because String objects are immutable they can be shared. For example: * <blockquote><pre> * String str = "abc"; * </pre></blockquote><p> * is equivalent to: * <blockquote><pre> * char data[] = {'a', 'b', 'c'}; * String str = new String(data);
上面一段摘自jdk8 String类的一段注释:
String类是常量,一旦被创建,值就不会被改变。StringBuffer支持可变长的字符串。因为String是不可变的,所以是可以被共享的。
所以代码:
String str = "abc";
和
char data[] = {'a', 'b', 'c'}; String str = new String(data);中的str都是表示"abc",它们实际指向的是同一个内存区域。换句话说,第二步创建str时,内存中已有字符串为"abc"的str存在,就不会重新分配地址,而是指向之前的。
例:
String str = "abc"; str = "abc" + "de";在执行第一行代码时jvm会自动给str分配一个内存区域,用于存放"abc"。
执行第二行代码时str会变成"abcde",但这个时候,"abcde"不是在第一次"abc"那个地址,jvm会重新给str分配一个地址,用于存放"abcde".之前存放"abc"的那个会被gc掉。
Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程。
StringBuffer
StringBuffer是可变长的,线程安全的
* A thread-safe, mutable sequence of characters. * A string buffer is like a {@link String}, but can be modified. At any * point in time it contains some particular sequence of characters, but * the length and content of the sequence can be changed through certain * method calls. * <p> * String buffers are safe for use by multiple threads. The methods * are synchronized where necessary so that all the operations on any * particular instance behave as if they occur in some serial order * that is consistent with the order of the method calls made by each of * the individual threads involved. * <p> * The principal operations on a {@code StringBuffer} are the * {@code append} and {@code insert} methods, which are * overloaded so as to accept data of any type. Each effectively * converts a given datum to a string and then appends or inserts the * characters of that string to the string buffer. The * {@code append} method always adds these characters at the end * of the buffer; the {@code insert} method adds the characters at * a specified point.
上面这段摘自jdk8 StringBuffer类的注解:
StringBuffer是一个线程安全,可变长的字符序列。StringBuffer类似于String,但是可以被修改,在任何时间点,它都包含一些特殊的字符序列,但是序列的长度和内容,可以通过调用一些方法来改变。
StringBuffer可以被多个线程使用,多个线程调用的方法都是同步的,这样所有在这个StringBuffer上的任何操作就像是有特定顺序的操作。
在StringBuffer上最主要的操作是append和insert方法,这两个方法也可以被重载来接受其他数据类型。
/** * Constructs a string buffer with no characters in it and an * initial capacity of 16 characters. */ public StringBuffer() { super(16); }
通常不指定长度的话,StringBuffer默认是16位。
StringBuffer sb = new StringBuffer();
也可在声明时指定长度:
/** * Constructs a string buffer with no characters in it and * the specified initial capacity. * * @param capacity the initial capacity. * @exception NegativeArraySizeException if the {@code capacity} * argument is less than {@code 0}. */ public StringBuffer(int capacity) { super(capacity); }
在append方法中有synchronized修饰,保证了线程安全
@Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } 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; }
在insert方法中也有修饰,
/** * @throws StringIndexOutOfBoundsException {@inheritDoc} * @since 1.2 */ @Override public synchronized StringBuffer insert(int index, char[] str, int offset, int len) { toStringCache = null; super.insert(index, str, offset, len); return this; } public AbstractStringBuilder insert(int index, char[] str, int offset, int len) { if ((index < 0) || (index > length())) throw new StringIndexOutOfBoundsException(index); if ((offset < 0) || (len < 0) || (offset > str.length - len)) throw new StringIndexOutOfBoundsException( "offset " + offset + ", len " + len + ", str.length " + str.length); ensureCapacityInternal(count + len); System.arraycopy(value, index, value, index + len, count - index); System.arraycopy(str, offset, value, index, len); count += len; return this; }
例:
StringBuffer sb = new StringBuffer().append("abc"); sb.append("de");
在上面两行操作中,sb始终指向一个内存区域,不存在重新分配地址,gc的过程,所以StringBuffer会比String要快。
StringBuilder
StringBuilder类是在jdk1.5引入的,这个类的方法不具有同步,这使得该类比StringBuffer类更高效。
* A mutable sequence of characters. This class provides an API compatible * with {@code StringBuffer}, but with no guarantee of synchronization. * This class is designed for use as a drop-in replacement for * {@code StringBuffer} in places where the string buffer was being * used by a single thread (as is generally the case). Where possible, * it is recommended that this class be used in preference to * {@code StringBuffer} as it will be faster under most implementations. * * <p>The principal operations on a {@code StringBuilder} are the * {@code append} and {@code insert} methods, which are * overloaded so as to accept data of any type. Each effectively * converts a given datum to a string and then appends or inserts the * characters of that string to the string builder. The * {@code append} method always adds these characters at the end * of the builder; the {@code insert} method adds the characters at * a specified point.上面这段摘自jdk8 StringBuilder类的注解:
StringBuilder是可变的字符序列,与StringBuffer提供了兼容的API,但是没有同步的保障。这个类的设计就是用来替代单线程使用StringBuffer的情况(而一般情况下都是单线程)。如果可以,我们推荐使用StringBuilder,因为StringBuilder在很多实现上逗比StringBuffer要快(因为它不用保证同步);
它的主要方法也是append和insert这些与上面StringBuffer都是类似的。
比较下它的append方法与上面StringBuffer的append方法:
@Override public StringBuilder append(String str) { super.append(str); return this; } 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; }
super.append(str);方法都是一致的,因为StringBuffer和StringBuilder都是继承自AbstractStringBuilder。唯一的区别就是append方法前面少了synchronized关键字
@Override public StringBuilder insert(int offset, String str) { toStringCache = null; super.insert(offset, str); return this; } public AbstractStringBuilder insert(int offset, String str) { if ((offset < 0) || (offset > length())) throw new StringIndexOutOfBoundsException(offset); if (str == null) str = "null"; int len = str.length(); ensureCapacityInternal(count + len); System.arraycopy(value, offset, value, offset + len, count - offset); str.getChars(value, offset); count += len; return this; }
3 总结
我们在使用字符串时,
如果是定长的考虑用String;
如果是变长的,在一般单线程环境下,优先使用StringBuilder;
如果是在多线程环境下,需要保证同步,则选择使用StringBuffer。