Java的字符串
String、StringBuilder、StringBuffer
特点:
- String 不可变
- StringBuilder 可变
- StringBuffer 可变、线程安全(方法加了同步锁)
性能:
- 每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象。
- StringBuffer 每次都会对StringBuffer对象本身进行操作。
- 相同情况下使用StringBuilder 相比使用StringBuffer 仅能获得10~15%左右的性能提升。
使用总结:
- 如果要操作少量的数据用 String;
- 单线程操作字符串缓冲区下操作大量数据 StringBuilder;
- 多线程操作字符串缓冲区下操作大量数据 StringBuffer。
String
赋值问题
String s1,s2,s3 = "abc", s4 ="abc" ; // 在常量池中 s3 和 s4 引用的内存地址一致
s1 = new String("abc"); // 在堆中分配内存
s2 = new String("abc");
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // true
System.out.println(s3 == s4); // true
System.out.println(s3.equals(s4)); // true
StringBuffer s5 = new StringBuffer("a"); // StringBuffer类中没有重写equals这个方法,因此这个方法就来自Object类,而Object类中的equals方法是用来比较 “地址”.
StringBuffer s6 = new StringBuffer("a");
System.out.println(s5 == s6); // false
System.out.println(s5.equals(s6)); // false
StringBuilder s7 = new StringBuilder("a"); // 理由同StringBuffer
StringBuilder s8 = new StringBuilder("a");
System.out.println(s7 == s8); // false
System.out.println(s7.equals(s8)); // false
知识点:
- new Object() 是从 堆 中分配内存;
- s3 = “abc” 指向常量池中 “abc” 地址;(这个涉及到字符串常量池相关内容)
- StringBuffer和 StringBuilder类中没有重新定义equals这个方法,直接调用的是Object中的equals() ,但是Object类中的equals方法是用来比较 “地址”;
- String中重写了equals(),比较字符串的值;
- == 对于引用类型来说,比较的是变量的引用地址;
final 与 不可变
final类:不可继承
final数组:不可改变其引用指向。
虽然 value[] 的指向不可变,但是value数组中的值是可变的。但是为什么String还是不可变呢?
String是不可变的关键都在底层的实现,String所有的方法里很小心的没有去动Array里的元素,没有暴露内部成员字段。而不是仅仅一个final。考验的是工程师构造数据类型,封装数据的功力。
从String源码中的public String replace(char oldChar, char newChar)
可以得看出,替换之后新 new了一个String进行返回。其实String方法只要涉及到改变字符串内容,全都是新返回一个String,这也保证了String的不变性。
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
不可变的优势
- 不会破坏Key的唯一性(Set, Map):如下代码1
- 线程安全(不能被写,读安全)
- 字符串常量池 :如下代码2
class Test{
public static void main(String[] args){
HashSet<StringBuilder> hs=new HashSet<StringBuilder>();
StringBuilder sb1=new StringBuilder("Alvin");
StringBuilder sb2=new StringBuilder("Alvinbbb");
hs.add(sb1);
hs.add(sb2); //这时候HashSet里是{"Alvin","Alvinbbb"}
StringBuilder sb3=sb1;
sb3.append("bbb"); //这时候HashSet里是{"Alvinbbb","Alvinbbb"}
System.out.println(hs);
//Output: ["Alvinbbb", "Alvinbbb"]
}
}
String one = "someString";
String two = "someString";
// 两者指向的是同一常量,则两者的引用的地址一致