前言
昨天跟老大面试一个中级开发,前面聊得不错,问到java基础时,问了一个经典的问题:String类为什么是不可变的?回答到:因为这个类被final修饰了,所以不可变,然后就没了。老大苦笑一下,面试直接结束。
String类是final的,这是毋容置疑的,但是这肯定不是面试官想听到的答案。回去我看了看String源码,想写写关于String、StringBuffer、StringBuilder这三者的关系。
正文
String类为什么是不可变的?
看源码会发现,无论是String还是StringBuffer或者StringBuilder他们类前都public final修饰了。所以说因为String类被final修饰了,所以不可变。这个答案肯定是不准确的。我们在创建字符串肯定通过构造方法。每个构造方法都需要value,关键就是这个value。
String类中value其实就是个byte[],但是这个byte[]数组被fianl修饰了,这就意味着数组一旦创建长度不可以改变,被final修饰的变量是不可变,也就是当这个数组指向某个对象后,它就不可以改变了,所以String是不可变的。
public String() {
this.value = "".value;
this.coder = "".coder;
}
@Stable
private final byte[] value;
StringBuffer/StringBuilder为什么是可变的?
StringBuffer/StringBuilder中value其实也是个就是个byte[],但是这个byte[]数组并没没有被fianl修饰,所以可以随意改变他的引用,当调用append()时,他会直接拼接到这个对象后面。StringBuffer和StringBuilder的初始化容量都是16,当容器满了会调用数组的arraycopy()方法拷贝原始数据,然后调用expandCapacity()进行可扩容,扩容大小为原大小的2倍+2。
public StringBuilder() {
super(16);
}
public StringBuffer() {
super(16);
}
byte[] value;
StringBuffer和StringBuilder的区别:
查看源码发现这两个类当中所有方法都是一样的,唯一不同的就是StringBuffer中的每个方法都比StringBuilder多一个修饰符synchronized(同步锁)。所以StringBuffer在多线程是安全的,StringBuilder是不全的。
public synchronized int compareTo(StringBuffer another) {
return super.compareTo(another);
}
public int compareTo(StringBuilder another) {
return super.compareTo(another);
}
总结:
String:不建议使用String操作字符串。
StringBuffer:适用于多线程操作字符串,安全,但是速度慢(有同步锁,多线程排队)。
StringBuilder:适用于单线程操作字符串,非多线程安全,但是速度快。