java中String、StringBuffer、StringBulider是经常要用到的字符串类,那么它们之间到底有什么不同?以及它们的应用场景?
String 类是不可变类,所以String本身的值是不能改变的,任何对String的操作都会引起新的String对象的产生.去看看String类的具体实现你就会发现,用来保存字符串的数组用final修饰:
下面的代码段是String类的部分实现.
private final char value[]; //保存字符串的数组.
String s="abcd";
s=s+1;
System.out.println(s); //result:abcd1;
这段代码大家一定已经用烂了,从结果来看,我们明明改变了String类型的变量s,为什么说String对象是不可变的呢,其实这是一种欺骗,JVM是这样解析这段代码的:首先创建对象s,赋予abcd,然后再创建一个新的对象s来执行第二行的代码,也就是说我们之前的对象s并没有改变.所以说String类型是不可变的对象了,由于这种机制,每当用String操作字符串时,实际上是不断的创建新的对象,所以效率会比较高,原来的对象会被GC回收掉.看下面这段代码:
String a="aaaaaa";
System.out.println(a.replace('a','b'); //result:bbbbbb
System.out.println(a); //result:aaaaaa
从结果可以看出,执行a.repalce(‘a’,’b’);并没有在a对象上操作,而是创建了一个新的对象. 因为最后a的值还是aaaaaa.
操作StringBuffer类型字符串时,每次结果都会对StringBuffer对象本身进行操作,而不是生成新的对象,而改变对象引用,所以一般情况推荐使用StringBuffer,特别是字符串对象经常改变的情况下.
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value; //保存字符串的字符数组.
上面是StringBuffer的部分实现,大家可以看到,它继承了AbstractStringBuilder,用可变的数组保存要操作的字符串.
StringBuffer b=new StringBuffer("aaaaaa");
System.out.println(b.replace(0,6,"bbbbbb"); //result:bbbbbb
System.out.println(b); result:bbbbbb;
从结果可以看出,执行b.replace(0,6,”bbbbbb”,b的内容也变成了bbbbbb,说明,字符串的替换是在b对象本身上进行的.
StringBuffer 对方法加了同步锁,或对调用的方法加了同步锁,所以是线程安全的.部分源码如下:
@Override
public synchronized int length() {
return count;
}
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
操作StringBuilder类型的字符串时,每次结果也都是在对象本身进行操作,不会产生新的对象.部分源码如下:
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;保存字符串的字符数组.
从上面的可以看出.它继承了AbstractStringBuilder,用可变的数组保存要操作的字符串.
StringBuilder b=new StringBuffer("aaaaaa");
System.out.println(b.replace(0,6,"bbbbbb"); //result:bbbbbb
从结果可以看出,执行b.replace(0,6,”bbbbbb”,b的内容也变成了bbbbbb,说明,字符串的替换是在b对象本身上进行的.
部分源码如下:
@Override
public int length() {
return count;
}
public AbstractStringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
下面是一个String,StringBuffer,StringBuilder速度测试代码:
方法是分别用String,StringBuffer,StringBulider连接2000000个字符串,看其执行时间,由于String特别慢,而StringBulider特别快,所以String连接了20000个,而只有在大量连接操作中,StringBuilder和StringBuffer的效率草拟体现出来,所以StringBuilder和StringBuffer都连接的是2000000个字符串. 最后测试了StringBuffer和StringBulider的赋值操作:
代码如下:
package thread;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/** * Created by yang on 16-7-10. */
public class StringTest {
public static String BASEINFO = "yang";
public static final int COUNT = 2000000;
/** * 执行一项String赋值测试 */
public static void doStringTest() {
String str = new String(BASEINFO);
long starttime = System.currentTimeMillis();
for (int i = 0; i < COUNT/100; i++) {
str = str + "xiyou";
}
long endtime = System.currentTimeMillis();
System.out.println((endtime - starttime)
+ " millis has costed when used String.");
}
/** * 执行一项StringBuffer赋值测试 */
public static void doStringBufferTest() {
StringBuffer sb = new StringBuffer(BASEINFO);
long starttime = System.currentTimeMillis();
for (int i = 0; i < COUNT; i++) {
sb = sb.append("miss");
}
long endtime = System.currentTimeMillis();
System.out.println((endtime - starttime)
+ " millis has costed when used StringBuffer.");
}
/** * 执行一项StringBuilder赋值测试 */
public static void doStringBuilderTest() {
StringBuilder sb = new StringBuilder(BASEINFO);
long starttime = System.currentTimeMillis();
for (int i = 0; i < COUNT; i++) {
sb = sb.append("miss");
}
long endtime = System.currentTimeMillis();
System.out.println((endtime - starttime)
+ " millis has costed when used StringBuilder.");
}
/** * 测试StringBuffer遍历赋值结果 * * @param mlist */
public static void doStringBufferListTest(List<String> mlist) {
StringBuffer sb = new StringBuffer();
long starttime = System.currentTimeMillis();
for (String string : mlist) {
sb.append(string);
}
long endtime = System.currentTimeMillis();
System.out.println(sb.toString() + "buffer cost:"
+ (endtime - starttime) + " millis");
}
/** * 测试StringBuilder迭代赋值结果 * * @param mlist */
public static void doStringBuilderListTest(List<String> mlist) {
StringBuilder sb = new StringBuilder();
long starttime = System.currentTimeMillis();
for (Iterator<String> iterator = mlist.iterator(); iterator.hasNext();) {
sb.append(iterator.next());
}
long endtime = System.currentTimeMillis();
System.out.println(sb.toString() + "builder cost:"
+ (endtime - starttime) + " millis");
}
public static void main(String[] args) {
doStringTest();
doStringBufferTest();
doStringBuilderTest();
List<String> list = new ArrayList<String>();
list.add(" I ");
list.add(" like ");
list.add(" xiyou ");
list.add(" linux ");
list.add(" xingqu ");
list.add(" xiaozu ");
list.add(" . ");
doStringBufferListTest(list);
doStringBuilderListTest(list);
}
}
结果如下:
1779 millis has costed when used String. //连接了20000个字符串
79 millis has costed when used StringBuffer. //连接了2000000个字符串.
53 millis has costed when used StringBuilder. //连接了2000000个字符串.
I like xiyou linux xingqu xiaozu. buffer cost:1 millis //7次循环赋值操作
I like xiyou linux xingqu xiaozu. builder cost:0 millis//7次循环赋值操作