StringBuffer和String的区别是什么
作者:邵发
官网:http://afanihao.cn/java
StringBuffer 是一个用于构造字符串的工具类,是面试里常考的一个问题。
1 字符串的拼接
还得先从String说起。在Java里,构造一个字符串可以直接用String的拼接功能,简单而直观。
比如,
String s1 = "阿发";
s1 += "你好";
s1 += "Hello";
s1 += "World";
看起来很简单,是不是?
但这一段代码存在一定的迷惑性,一般初学者会觉得,这4行代码修改了String对象只有一个,就是s1对象。
其实不然,这里是创建了4个String对象。也就是说,
s1 += "你好" ;
相当于:
String str = s1 + "你好";
s1 = str;
其中,s1 + "你好" 是创建一个新的String对象。
通过单步调试,观察s1引用的实例ID值,可以更清楚的认识上述结论。
此时,s1对象的实例ID为19。每一个Java实例,都有一个不同的ID。
继续运行,s1的实例ID为29,值变成了"阿发你好"。说明此时产生了一个新的String对象(id=29的实例)
继续运行,s1的实例ID为31,说明又创建了一个新的String对象。
最后,又创建了一个String对象,实例id=33,值为"阿发你好helloworld"
这个演示告诉我们,字符串拼接 s1 = s1 + "XXX" 之后,实际上是创建了新的String对象。一共创建了4个String对象,前3个由于失去了引用,将会被GC自动回收。
2 效率问题
一般认为,使用String进行字符串拼接存在效率问题。怎么会有效率问题呢?举一个例子。
比如,我们要求对一个字符串的$字符进行转义,在$字符前端添加一个\。
示例:
原文 var a = ${name}
转义后 var a = \${name}
用代码实现为:
public static String escape(String source)
{
String result = "";
for(int i=0; i<source.length(); i++)
{
char ch = source.charAt(i);
if( ch == '$')
result += '\\'; // 附加一个反斜杠
result += ch;
}
return result;
}
public static void main(String[] args) throws Exception
{
String source = "var a = ${name}";
String result = escape(source);
System.out.println(result);
}
根据在第1节中的阐述,在escape()中每执行一次 result += xxx 就是抛弃了原有result对象,创建了一个新的result对象。在这里,一共执行了16次拼接操作,所以一共创建并抛弃了16个String对象。
现在放大这个问题的规模,如果输入长度为1000的字符串进行处理,则至少创建并抛弃1000个String对象。这显然效率上有点问题,因为过于频繁的抛弃对象交给GC来处理,对GC也是一个负担啊。
3 StringBuffer
StringBuffer,是一个字符串的构造工具。可以认为它内部包含了一个字符数组,可以修改里面的字符的值。
例如,StringBuffer的基本用法示例:
// 创建一个初始容量为100的Buffer,如果实际内容超过容量则内部会自动扩展容量
StringBuffer sbuf = new StringBuffer(100);
// 往里面塞内容
sbuf.append("阿发");
sbuf.append("你好");
sbuf.append("Hello");
sbuf.append("World");
// 最终构造的字符串
String result = sbuf.toString();
其实一看到Buffer字样,我们应该明白它是什么意思。它是一个容器,一个缓冲区,可以存放字符。自然地,当使用append()往容器里塞东西的时候,并没有什么效率问题。
下面用StringBuffer来实现上述的转义功能。
public static String escape2(String source)
{
StringBuffer sbuf = new StringBuffer(source.length()*2); // 最好预估一下容量
for(int i=0; i<source.length(); i++)
{
char ch = source.charAt(i);
if( ch == '$')
sbuf.append('\\'); // 附加一个反斜杠
sbuf.append(ch);
}
return sbuf.toString();
}
把一个一个字符塞到一个容器里,会有效率问题吗,当然没有。会创建1000个String对象吗,当然不会。这便是StringBuffer在处于字符串构造问题时的优势所在。
StringBuffer就像一个数组一样,其他用法可以自己百度一下。比如,StringBuffer可以修改某一个置的字符。sbuf.setCharAt(..) 。它的容量可以自动扩展,不过最好在初始化的时候就预估一个容量。
4 结论
当需要执行大量的字符拼接构造问题时,应考虑使用 StringBuffer 来实现,以优化执行效率。
当只进行少量拼接操作时,还是直接使用String吧,别大费周章创建StringBuffer了。