String、StringBuffer、StringBulider的区别
0、简单介绍:
共同点:
-
都是java.long包下的
-
都是final类,不允许被继承
// 源码
// String类实现了Serializable、Comparable、CharSequence
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {}
//StringBuffer类实现了Serializable、CharSequence 继承了AbstractStringBuilder
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {}
// StringBulider类实现了Serializable、CharSequence 继承了AbstractStringBuilder
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence{}
不同点:主要从执行速度和安全这两个方面来看
1、执行速度
一般情况下:StringBulider > StringBuffer > String
为什么是这个样子的呢?
因为 String 为字符常量,StringBulider和StringBuffer为字符串变量,若是使用String创建对象,这个对象是不能变的,但是后两个是变量,是可以变得。详细看下面的案例:
public static void main(String[] args) {
String s1 = "hello";
System.out.println(s1);
s1 = s1 + "world";
System.out.println(s1);
}
执行结果:
hello
helloworld
这个时候大家就要说了,这s1对象不是可以变吗?这个时候你就掉进了一个大坑里面了!
这个是由于JVM对这几行代码的优化。其实是首先创建一个s1 = “hello”,在执行到s1 = s1 + "world"
的时候,JVM又创建了一个新的对象名为str,然后把原来的str值和“hello”加起来在赋值给新的str,原来的str就被JVM的垃圾回收机制给回收了,所以,实际上str没有改变,即为String对象一旦创建就不能被更改。所以String对象在创建的时候不断的新建对象和回收就对象,所以执行很慢。
看看String的源码:
// final修饰的数组,为常量,一旦赋值就不能改变,这个就是不能被修改成的原因
private final char value[];
public String() {
this.value = "".value;
}
public String(String original) {
// 把原字符串original切分成字符数组并赋给value[];
this.value = original.value;
}
再来看看StringBuffer的源码:StringBuffer和StringBulider都是继承的AbstractStringBuilder类:
// StringBuffer的构造方法
public StringBuffer() {
super(16);
}
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
// 进入super方法
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
char[] value;
// 这个时候就可以看出来StringBuffer类里面的value数组为普通的数组,没有被final修饰
// 而且可以通过append()方法将新字符串加入value[]末尾。这样也就改变了value[]的内容和大小了。
总结,讨论String和StringBuffer可不可变。本质上是指对象中的value[]字符数组可不可变,而不是对象引用可不可变。
看看下面这两段代码猜猜哪个更快呢?
String s1 = "hello" + "world";
StringBuffer s2 = new StringBuffer("hello").append("world");
你会发现上面的快,这是为什么呢, 这是因为第1行中的操作和String str=“abcde”;是完全一样的,所以会很快,而如果写成下面这种形式 :
String s1 = "hello";
String s2 = "world";
String s3 = s1 + s2;
执行上面这段代码,JVM就会不断的进行创建、回收对象的操作。速度就会慢。
StringBuffer和StringBuilder可谓双胞胎,StringBuilder是1.5新引入的,其前身就是StringBuffer。 StringBuilder的效率比StringBuffer稍高,这两者的方法没有很大区别。但在线程安全性方面,StringBuffer允许多线程进行字符操作。
2、线程安全
StringBuffer源码:
synchronized StringBuffer append(AbstractStringBuilder asb) {
toStringCache = null;
super.append(asb);
return this;
}
public synchronized StringBuffer append(char[] str, int offset, int len) {
toStringCache = null;
super.append(str, offset, len);
return this;
}
// 后面的方法省略,具体可以查看完整源码
......
StringBulider源码:
public StringBuilder append(String str) {
super.append(str);
return this;
}
public StringBuilder append(char[] str, int offset, int len) {
super.append(str, offset, len);
return this;
}
// 后面的方法省略,具体可以查看完整源码
......
观察源码你会发现: StringBuffer的很多方法都被关键字synchronized 修饰了,而StringBuilder没有。
synchornized保证了线程同步
3、总结
-
在编译阶段就能够确定的字符串常量,完全没有必要创建String或StringBuffer对象。直接使用字符串常量的"+"连接操作效率最高。
-
StringBuffer对象的append效率要高于String对象的"+"连接操作。
-
不停的创建对象是程序低效的一个重要原因。那么相同的字符串值能否在堆中只创建一个String对象那。显然拘留字符串能够做到这一点,除了程序中的字符串常量会被JVM自动创建拘留字符串之外,调用String的intern()方法也能做到这一点。当调用intern()时,如果常量池中已经有了当前String的值,那么返回这个常量指向拘留对象的地址。如果没有,则将String值加入常量池中,并创建一个新的拘留字符串对象。
简洁版:
-
String : 会产生临时的字符串对象
-
执行速度:StringBulider > StringBuffer > String
-
StringBuffer是线程安全的,StringBulider不是线程安全的
怎么使用:
-
String:适用于少量的字符串操作的情况
-
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
-
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
博客参考: