String
-不可变类,属性value为不可变数组,即String初始化构造器没有初始容量为16的概念,你定义多少,String中字符数组的长度就是多少,不存在字符数组扩容一说
源码:
其中除了hash,其它属性全被final修饰,
且value只能被初始化一次。这里的value变量其实就是存储了String字符串中的所有字符。
substring方法源码
类似的我们可以看到,String类的concat方法,replace方法,都是内部重新生成一个String对象的。
这就是为什么采用String对象频繁的进行拼接,截取,替换操作效率很低下的原因
StringBuilder
-可变字符,内部可变数组,存在初始化StringBuilder对象中字符数组容量为16,存在扩容。
首先看下StringBuilder的构造方法
在参数Str 数组长度的基础上再增加16个字符长度,作为StringBuilder实例的初始数组容量,并将str字符串 append到StringBuilder的数组中。
具体看下父类AbstractStringBuilder的append方法
1:首先判断append的参数是否为null,如果为null的话,这里也是可以append进去的
其中ensureCapacityInternal方法是确保这次append 的时候StringBuilder的内部数组容量是满足的,即这次要append的null字符长度为4,加上之前内部数组中已有的字符位数c之后作为参数执行。
2:如果不为null的话,就获取这次需要append的str的字符长度。紧接着执行是否需要扩容的方法
3:重点看下append方法的关键:String的 getChars方法(从str的0位开始,到str的长度,当前StringBuilder对象的字符数组,当前数组已有的字符长度)
这里的toString方法直接new 一个String对象,将StringBuilder对象的value进行一个拷贝,重新生成一个对象,不共享之前StringBuilder的char[]
没有像String一样去重新new 对象,所以在频繁的拼接字符上,StringBuilder的效率远远高于String类。
StringBuffer
- 可变字符,内部可变数组,存在初始化StringBuilder对象中字符数组容量为16,存在扩容。
和StringBuilder一样,也不用多说,重点看下其append方法:
这里就是在append方法上加了同步锁,来实现多线程下的线程安全。其他的和StringBuilder一致。比StringBuilder多了一个参数toStringCache,其作用是去缓存toString
StringBuffer的toString方法
其new出来的String对象是会共享同一个char[] 内存的,达到共享的目的。但是StringBuffer只要做了修改,其toStringCache属性值都会置null处理。这也是StringBuffer和StringBuilder的一个区别点。
通过反射实现修改String
通过反射修改String 类的属性value
代码:
public class Main {
public static void main(String[] args) {
//实践传值修改String
String string = "我不该生气吗?";
System.out.println(string);
modifyString(string);
System.out.println(string);
}
public static void modifyString(String string) {
try {
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
field.set(string, field.get("我该如何面对你?"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
总结:
String 类不可变,内部维护的char[] 数组长度不可变,为final修饰,String类也是final修饰,不存在扩容。字符串拼接,截取,都会生成一个新的对象。频繁操作字符串效率低下,因为每次都会生成新的对象。
StringBuilder 类内部维护可变长度char[] , 初始化数组容量为16,存在扩容, 其append拼接字符串方法内部调用System的native方法,进行数组的拷贝,不会重新生成新的StringBuilder对象。非线程安全的字符串操作类, 其每次调用 toString方法而重新生成的String对象,不会共享StringBuilder对象内部的char[],会进行一次char[]的copy操作。
StringBuffer 类内部维护可变长度char[], 基本上与StringBuilder一致,但其为线程安全的字符串操作类,大部分方法都采用了Synchronized关键字修改,以此来实现在多线程下的操作字符串的安全性。其toString方法而重新生成的String对象,会共享StringBuffer对象中的toStringCache属性(char[]),但是每次的StringBuffer对象修改,都会置null该属性值。