面试题:String,StringBuilder和StringBuffer的区别?
String是不可变类,StringBuffer和StringBuilder是可变类;String在多次字符串拼接时效率低,且线程不安全,StringBuilder效率最高,但是线程不安全,StringBuffer效率在前两者其中,且是线程安全。
源码分析:
1.String
(1)类定义
public final class String implements java.io.Serializable, Comparable<String>, CharSequence
String类是个final类,实现了Serializable(序列化接口),Comparable(比较大小接口)和CharSequence(只读字符序列接口)。String和其他八个基本数据类型的包装类共同为不可变类。
(2)主要变量
private final char value[];
String类的底层基本是char类型数组,String的一些基本方法也是调用char数组的基本方法。
(3)主要构造方法
public String() { this.value = "".value; }
String的默认值为"";
(4)String常量池
jvm在启动时就会加载的一块空间,符串常量池的特点就是有且只有一份相同的字面量,如果有其它相同的字面量,jvm则返回这个字面量的引用,如果没有相同的字面量,则在字符串常量池创建这个字面量并返回它的引用。通过使用new关键字获取的对象会放在堆里,而不会加载到字符串常量池,intern()方法能使一个位于堆中的字符串在运行期间动态地加入到字符串常量池中。
(5)字符串拼接
String字符串拼接一般通过用+号实现,正常情况下有两种形式:
String a = "ab" + "cd";
此时在JVM在编译时就认为这个加号是没有用处的,编译的时候就直接变成abcd,即使是后面添加多个字符串效率不会比StringBuiler或者StringBuffer慢。
String a = "ab"; String b = "cd"; String c = a + b;
此时字符串拼接系统会优化成通过new StringBuiler,进行两次append操作,该操作是在堆中进行,如果是进行多次拼接,会产生多个StringBuiler对象,效率自然会降低,但是如果是在同一行代码里做拼接操作,只是额外new了一个StringBuiler对象,效率也不会慢。
2.StringBuilder
(1)类定义
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence abstract class AbstractStringBuilder implements Appendable, CharSequence
StringBuilder类是final类,实现了Serializable(序列化接口),CharSequence(只读字符序列接口),其父类实现了CharSequence(只读字符序列接口)和Appendable(拼接接口),该类是可变类。
(2)主要变量
private final char value[];
StringBuilder的底层基本实现和String是一致的
(3)主要构造方法
public StringBuilder() { super(16); } AbstractStringBuilder(int capacity) { value = new char[capacity]; }
StringBuiler类的基本构造方法是一个容量为16的字符串数组
(4)字符串拼接
public StringBuilder append(String str) { super.append(str); return this; } public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; } private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) { value = Arrays.copyOf(value, newCapacity(minimumCapacity)); } }
内部的实现是通过char数组操作的,如果超过容量,会发生通过数组拷贝方式的扩容操作。
(5)线程安全性
从上面的源码解析,count += len;并非原子操作,如果两个线程同时访问到这个方法,那么AbstractStringBuilder中的count是不是就是相同的,所以这两个线程都是在底层char数组的count位置开始append添加,那么最终的结果肯定就是在后执行的那个线程append进去的数据会将前面一个覆盖掉,所以StringBuilder在字符串拼接操作是线程不安全的。由于String的拼接也是通过StringBuiler来实现,所以String的字符拼接也不是线程安全的
3.StringBuffer
(1)类定义
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence
StringBuffer其父类实现了Serializable(序列化接口),CharSequence(只读字符序列接口),与StringBuilder有公共的父类AbstractStringBuilder,该类是可变类。
(2)主要变量
StringBuffer的底层基本实现和StringBuilder是一致的。
(3)构造方法
StringBuffer的构造方法和StringBuilder是一致的。
(4)字符串拼接
public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; }
StringBuffer字符串拼接基本与StringBuilder内部实现是一致的,主要是区别是在方法体上通过synchronized关键字加锁。
(5)线程安全性
从上面的源码解析,由于在方法加上synchronize关键字,所以字符串拼接操作是线程安全。