string stringbuff stringbuild的执行效率: stringbuild>stringbuff>string
String类是不可变类,任何对String的改变都会引发新的String对象的生成;
StringBuffer是可变类,任何对它所指代的字符串的改变都不会产生新的对象,线程安全的。
StringBuilder是可变类,线性不安全的,不支持并发操作,不适合多线程中使用,但其在单线程中的性能比StringBuffer高。
栈:存放基本类型的变量数据和对象的引用。像int a = 1; String str = "hello" ; String str1 = new String("OK") ; 栈中存放的是 a, 1, str, str1。
常量池:存放基本类型常量和字符串常量。
堆:存放所有new出来的对象。
栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会自动消失。堆中的对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定,具有很大的灵活性。
而对于字符串来说,其对象的引用都是存储在栈中的,如果是编译期已经创建好(即指用双引号定义的)的就存储在常量池中,如果是运行期(new出来的对象)则存储在堆中。对于equals相等的字符串,在常量池中是只有一份的,在堆中则有多份。
举例看下吧:String str1="abc";
String str2="abc";
String str3="abc";
String str4=new String("abc");
String str5=new String("abc");
String str5=new String("abc");
对于浅蓝色箭头,通过new操作产生一个字符串(“abc”)时,会先去常量池中查找是否有“abc”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此“abc”对象的拷贝对象,所以,对于String str=new String("abc"),如果常量池中原来没有"abc"则产生两个对象,否则产生一个对象。
这里插入一段题外话:有 int a= 3; int b = 3;编译器首先处理int a = 3;先是在栈中建立一个变量为a的引用,然后在栈中查找是否有字面值为3的地址,若没找到,就在栈中开辟一个空间来存放3这个字面值的地址,然后将a指向3的地址。接下来编译器处理int b = 3;同样在栈中建立b的引用变量后,由于在栈中找到了有3这个字面量的值,便直接将b指向了3的地址,这样a,b都同时指向了3这个字面量地址。如果我们接下来声明 a = 4;那会发生什么呢?这时在编译器内部,它会重新搜索栈中是否存在有4这个字面值,若有,则将a指向这个4字面值的地址,若没有,则在栈中自己开辟一个,然后指向它,不会影响b的指向。
而对于String str1 = "abc"; Sring str2 = "abc"; String str3 = new String("abc"); 编译器的处理是这样的: 先遇到String str1 = "abc"; 首先在栈中建立一个str1的引用,然后在常量池中查找是否存放为"abc"的地址("abc"存放在常量池中,引用在栈中),找到就指向它,没有就在常量池中造一个,然后也指向它。接着Sring str2 = "abc"; 因为常量池有了"abc",所以str2就也指向它,最后到了String str3 = new String("abc"); 先在栈中建立一个引用str3,然后去常量池中查找是否有“abc”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此“abc”对象的拷贝对象,所以,对于String str=new String("abc"),如果常量池中原来没有"abc"则产生两个对象,否则产生一个对象。
Java采用这些机制的目的也就是希望节省内存空间了,经常用到的而且又是不怎么变化的数据,就让大家一起分享!