很多小伙伴经常使用到string,stringBuffer和StringBuilder,但对他们之间的区别却是很难说的上来。今天我将着手从源码和内存分析这两个层面来解说一下他们之间的区别。
下面开始上代码:
String str = "aaa";
String str1 = new String("sdf");
StringBuilder stringBuilder = new StringBuilder(str);
StringBuffer stringBuffer = new StringBuffer(str);
从这段代码可以看出他们之间对象名就有着不同(手动狗头),下面我们进入他们的源码来分别看一下他们之间的不同。
string源码:
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
public String() {
this.value = "".value;
}
/**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of {@code original} is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
* @param original
* A {@code String}
*/
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
从上面这段代码中我们可以看到string不管是空参构造还是有参构造都使用了value
这个属性,在看value
的属性定义private final char value[]; 竟然是一个常量数组。这意味着char数组长度不可改变,同样也意味着string的内容是不可修改的。在需要修改string内容时,只能通过在开辟新的堆内存,然后把两个堆内存的内容,在一个新建的内存中拼接。这将会产生大量的垃圾空间,使性能下降。在少量操作数据时可以使用(因为简单)。了解了这个我们来看下一个,StringBuilder
继续上源码:
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/** use serialVersionUID for interoperability */
static final long serialVersionUID = 4383685877147921099L;
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
/**
* Constructs a string builder with no characters in it and an
* initial capacity specified by the {@code capacity} argument.
*
* @param capacity the initial capacity.
* @throws NegativeArraySizeException if the {@code capacity}
* argument is less than {@code 0}.
*/
public StringBuilder(int capacity) {
super(capacity);
}
/**
* Constructs a string builder initialized to the contents of the
* specified string. The initial capacity of the string builder is
* {@code 16} plus the length of the string argument.
*
* @param str the initial contents of the buffer.
*/
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
/**
* Appends the specified {@code StringBuffer} to this sequence.
*
* The characters of the {@code StringBuffer} argument are appended,
* in order, to this sequence, increasing the
* length of this sequence by the length of the argument.
* If {@code sb} is {@code null}, then the four characters
* {@code "null"} are appended to this sequence.
*
* Let n be the length of this character sequence just prior to
* execution of the {@code append} method. Then the character at index
* k in the new character sequence is equal to the character at
* index k in the old character sequence, if k is less than
* n; otherwise, it is equal to the character at index k-n
* in the argument {@code sb}.
*
* @param sb the {@code StringBuffer} to append.
* @return a reference to this object.
*/
public StringBuilder append(StringBuffer sb) {
super.append(sb);
return this;
}
@Override
public StringBuilder append(CharSequence s) {
super.append(s);
return this;
}
上面这段源码无法看出StringBuilder
的属性是否是常量,构造方法也只是使用了它父类的构造,那么就看它继承的AbstractStringBuilder
的代码。但可以看出StringBuilder
的方法上没有线程安全处理,这意味着StringBuilder
在多线程环境下是不安全的,但在单线程环境下效率高,可以在同一堆内存修改字符串的值,不会产生多余的垃圾空间。
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;
/**
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
}
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
从这段代码中我们可以看到AbstractStringBuilder
的构造方法使用的属性value
,不是常量数组,这意味着char数组长度可改变,同样也意味着AbstractStringBuilder
的内容是可修改的,那么继承它构造的StringBuilder
的内容也是可以修改的。接下来看StringBuffer
,源码如下:
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
/** use serialVersionUID from JDK 1.0.2 for interoperability */
static final long serialVersionUID = 3388685877147921107L;
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
/**
* 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);
}
@Override
public synchronized int length() {
return count;
}
@Override
public synchronized int capacity() {
return value.length;
}
@Override
public synchronized void ensureCapacity(int minimumCapacity) {
super.ensureCapacity(minimumCapacity);
}
/**
* @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);
}
StringBuffer
构造方法同样使用了它父类AbstractStringBuilder
的构造,同时可以看出StringBuffer
的方法上都有线程安全处理,这意味着StringBuffer
在多线程环境下是安全的,但在单线程环境下效率相对StringBuilder
要低很多,同样可以在同一堆内存修改字符串的值,同样不会产生多余的垃圾空间。
总结一下:
String:
值不可修改,多线程环境下,线程安全(final修饰),字符串拼接效率低(少量数据可用)
StringBuffer
值可修改,多线程环境下,线程安全(synchronized修饰每个方法),单线程环境字符串拼接效率低
StringBuilder
值可修改,多线程环境下,不线程安全,单线程环境字符串拼接效率高