字符修改
String是不可变字符序列(修改String类时,会新增String对象);StringBuffer和StringBuilder进行修改时不会产生新的对象
UML图解
线程安全
String由于不可变,所以是线程安全;
String和StringBuffer类中每个方法都用synchronized修饰所以是线程安全的;
StringBuilder并不是线程安全的
解释: String不可变,无论是多线程还是单线程都是安全的;StringBuffer每个方法都加了synchronized,无论多线程还是单线程都是安全的;StringBuilder是可变字符序列,没有加上同步锁;是线程不安全的
执行效率
String由于每次修改都会创建新的对象,所以在操作大量数据时效率最慢
StringBuffer由于每个方法都加了synchronized修饰,所以每次执行都需要等待锁释放,效率居中
StringBuilder效率最快
String
String 使用一个final修饰的char数组value维护,value被final修饰不可改变,这也就是String为什么是不可变字符序列
String 真的不可变吗?
由上可知,String的成员变量都是用private final修饰的,初始化后不可以改变。但是value比较特殊,它是一个引用变量,并不是真正的对象。value是final修饰的,也就是说value不能再指向其他数组对象,但我们可以改变value指向的数组中的值。
使用普通的代码无法做到,因为我们不能访问到value的引用,更不能通过引用去修改数组。我们可以使用反射访问私有成员,进而改变value所指向数组的结构
public static void main(String[] args) throws Exception {
//创建字符串"Hello World", 并赋给引用s
String s = "Hello World";
System.out.println("s = " + s); //Hello World
System.out.println("修改前的str的内存地址" + System.identityHashCode(s));
//获取String类中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");
//改变value属性的访问权限
valueFieldOfString.setAccessible(true);
//获取s对象上的value属性的值
char[] value = (char[]) valueFieldOfString.get(s);
//改变value所引用的数组中的第5个字符
value[5] = '_';
System.out.println("s = " + s); //Hello_World
System.out.println("修改前的str的内存地址" + System.identityHashCode(s));
}
StringBuffer和StringBuilder
StringBuffer和StringBuilder都继承了AbstractStringBuilder,他们存储字符的字符数组来自父类的value,这个value并不是final类,可以被修改;对StringBuffer和StringBuilder进行修改可以使用append的方法,在原有的字符序列后面加上新增的字符值(父类AbstractStringBuilder中的方法可以看出这样的操作);所以也就解释了为什么StringBuffer和StringBuilder为什么是可变字符序列
StringBuilder append(String str) 方法源码分析
StringBuilder
AbstractStringBuilder