三者区别是什么?
总的来说:String 是个字符串常量,StringBuilder是一个非线程安全的字符串变量,StringBuffer是一个线程安全的字符串变量。
源码分析:
String是一个不可变的char[] 数组。源码如下:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
那么我们经常使用的"+"连接两个String,不就得到一个改变的String了吗,这是怎么回事呢?
测试代码如下:
public class StringCode {
String s1="ni "+"hao "+"ma ?";
String s2="ni ";
String s3="hao ";
String s4="ma ?";
String s5=s2+s3+s4;
}
反汇编得到代码:
public com.example.demo.code.StringCode();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String ni hao ma ?
7: putfield #3 // Field s1:Ljava/lang/String;
10: aload_0
11: ldc #4 // String ni
13: putfield #5 // Field s2:Ljava/lang/String;
16: aload_0
17: ldc #6 // String hao
19: putfield #7 // Field s3:Ljava/lang/String;
22: aload_0
23: ldc #8 // String ma ?
25: putfield #9 // Field s4:Ljava/lang/String;
28: aload_0
29: new #10 // class java/lang/StringBuilder
32: dup
33: invokespecial #11 // Method java/lang/StringBuilder."<init>":()V
36: aload_0
37: getfield #5 // Field s2:Ljava/lang/String;
40: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
43: aload_0
44: getfield #7 // Field s3:Ljava/lang/String;
47: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
50: aload_0
51: getfield #9 // Field s4:Ljava/lang/String;
54: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
57: invokevirtual #13 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
60: putfield #14 // Field s5:Ljava/lang/String;
63: return
}
我们看到, String s1="ni "+"hao "+"ma ?";
这种情况,编译器直接认为是 String s1="ni hao ma ?";
而 String s5=s2+s3+s4;
这种情况,就等同于
String s5=new StringBuilder().append(s1).append(s2).append(s3).toString();
所以前一种情况,不可变字符串内容便是"ni hao ma ?",而后一种是通过StringBuilder.append.toString实现。
我们在看toString源码:
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
所以toString其实是new了一个新的对象。
如此,如果在for循环种使用了+去拼接字符串,每次循环都会进行一次new StringBuilder().和new String,产生两个对象。从而增加了系统负担。
所以代码中建议使用StringBuilder或StringBuffer去拼接字符串。
StringBuilder是怎么实现的?
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence{
.....
}
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
......
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
......
}
StringBuilder继承自AbstractStringBuilder,而在AbstractStringBuilder中定义了一个可变的char[]数组,并实现了数据扩容的方法。从而实现了可变字符串。
StringBuffer是怎么做到线程安全的?
看源码:
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
......
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
@Override
public synchronized int capacity() {
return value.length;
}
@Override
public synchronized void ensureCapacity(int minimumCapacity) {
super.ensureCapacity(minimumCapacity);
}
@Override
public synchronized void trimToSize() {
super.trimToSize();
}
}
从源码很明显可以看出,StringBuffer也继承自AbstractStringBuilder,线程安全是通过将方法加synchronized对象锁实现的。