- StringBuffer和StringBuilder的append比string的"+""快
String str = "a";
str+="b";
我们可以看到,初始String值为"a",然后在这个字符串后面加上新的字符串"b",这个过程是需要重新在栈堆内存中开辟内存空间的,最终得到了“ab”字符串也相应的需要开辟内存空间,这样短短的两个字符串,却需要开辟三次内存空间,不得不说这是对内存空间的极大浪费。为了应对经常性的字符串相关的操作,引入了两个新的类——StringBuffer类和StringBuild类来对此种变化字符串进行处理。
内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的
从javap的解析结果可以看出,在常量池中生成了 a 和 b两个字符串对象,并没有ab这个字符串对象。在堆中用两个new创建了两个String对象,然后通过StringBuilder对象调用append方法进行两次字符串的添加操作,最后用StringBuilder的toString方法进行返回了一个堆中的字符串对象
(jdk9以后版本的虚拟机,则是调用虚拟机自带的InvokeDynamic拼接字符串,并且保存在堆中)。
所以,Strin str = new String(“a”) + new String(“b”) 在堆中创建了三个字符串对象(a字符串对象,b字符串对象,ab字符串对象),在堆中的常量池中创建了2个字符串对象(a字符串对象,b字符串对象),一共创建了5个对象。
StringBuilder是对对象自身进行操作,堆中只会创建一个对象。
- 但如果变成以下,string就比StringBuild和StringBuffer快
String str = "a"+"b";
String str =“a” + “b” 创建了1个字符串对象,在java堆中的常量池中
这是因为String和我们其它类型的变量不同,其它的非基本类型对象的值、数据都存储在java的堆中,而String类型的变量的值是存储jvm在方法区中的字符串常量池中的。(和jvm有关)当我们执行:String str = “a”+“b”;这句话的时候,String会自动把这个对象的值看成"ab",然后在方法区中如果找到了值同样为"ab"的,就会直接让str句柄指向它,也就是说,我们的这句代码现在相当于:
String str = "ab";
String常量的累加操作中,编译器会进行优化,如果是 String str = “a” + “b”,那相当于String str = “ab”; ,常量池中会创建一个ab的对象。 ldc #2 // String ab 然后 将int,float或String型常量值从常量池中推送至栈顶即可。等于只在常量池中创建了一个对象,jdk1.8的常量池在java堆中,等于在java堆的常量池创建了一个对象。
这可比之前的String用法一遍遍反复地去创建对象回收对象快多了,因此,即使重复10万次依然还是很快。
String str = new String(“a”) + new String(“b”) 创建了5个字符串对象,3个在java堆中,2个在java堆的常量池中。
String s1 = new String(“str”);建立两个对象
在堆中创建保留对象,在栈中保留地址,在常量池中存放一份复制的对象
-
StringBuffer线程安全 源代码上有synchronized
StringBuilder没有 -
总结:
如果要操作少量的数据用 String;
多线程操作字符串缓冲区下操作大量数据 StringBuffer;
单线程操作字符串缓冲区下操作大量数据 StringBuilder。