String s1 = "123";
String s2 = "123";
//s1==s2 true
String s3 = new String("234");
String s4 = new String("234");
//s3==s4 false
为什么String==String?
在JVM中有一块区域叫做常量池,常量池中的数据是在那些在编译期间被确定,并被保存在已编译的.class文件中的一些数据
除了包含8种基本的类型外,还有String及其数组的常量值,另外还有一些以文本的形势出现的符号引用
java栈的特点是存取速度快,但是空间小,数据生命周期固定,只能生存到方法的结束。
我们定义的boolean b = true; cha c = 'c' ; String str = "123"; 这些语句,我们拆分为几部分来看:
- true,c,123 这些等号右边的指向编译期间可以被确定的内容,都被维护到常量池中
- b,c,str 这些等号左边第一个出现的指向是一个引用,引用的内容是等号右边数据在常量池中的地址
- boolean char String 这些事引用的类型
栈有个特点,就是数据共享,回到我们第一个例子
String s1 = "123"; 编译的时候,在常量池中创建了一个常量 “123”,然后 下一行 String s2 = "123";先去常量池中找找有没有“123”,发现有,s2也指向了常量池中的“123”,所有s1==s2 返回的就是true,因为s1和s2都指向的是常量池中的“123”这个字符串地址,当然如果String s2 = "234",就又不一样了,因为常量池中没有“234”,所有会在常量池中创建一个“234”,然后s2代表是“234”的地址。
分析了String,其实其他的基本数据类型也都是一样的:先看常量池中有没有要创建的数据,有就返回数据的地址,没有就创建一个
下面一个例子
String s3 = new String("234");
String s4 = new String("234");
System.out.println(s3==s4);
//false
为什么false呢?因为:java虚拟机的解释器每遇到一个new的关键字,都会在内存中开辟一块内存存放String对象,所有s3 s4指向的堆内存虽然存储的都是“234”,但是由于是2块不同的 堆内存,所有返回的是false
为什么要用StringBuffer和StringBuilder来拼接字符串?
String str = "123";
str+="111";
str+="222";
System.out.println(str);
//输出 123111222
编译器每次碰到“+”的时候,会new一个StringBuilder出来,接着调用append()方法,再调用toString()方法,生产新的字符串
那么,假如:代码中有很多 +,就会每个+ 生成一次StringBuilder,这种方式对内存是一种浪费,效率很不好。
String还有一种方法 concat()方法,通过2次字符串的拷贝,产生新的 数组,在根据数据new出一个新的String对象,这以为concat方法调用N此,将发送N*2次的数组拷贝,及new出对象,效率很低。
那么我们就可以用Stringbuffer和StringBuilder来
Stringbuffer和StringBuilder原理一样,无非就是在底层维护一个char数据,每次append的时候就往char数据里面放字符而已,在最终sb.toString()的时候,用一个new String()方法 把char数据里面的内容转换成String,这样整个过程只产生了一个StringBuilder对象和一个String对象,非常节省空间
StringBuffer是线程安全的,StringBuilder是线程非安全的,所以在不涉及线程安全的前提下,尽量在方法内部使用StringBuilder