1. 前言
疑问:String,StringBuffer,StringBuilder三者的区别是什么?应用场景分别是什么呢?
疑问:String为啥要定义为不可变的呢?它是如何实现的呢?
三者的继承关系如下:
2. String
2.1 为什么不可变
比如我们有n个变量指向的是同样内容的字符串,
String one = "someString";
String two = "someString";
……
String n = "someString";
因为String是对象,如果定义n次就要开辟n块内存,还是对于这n个重复的内容,岂不是很浪费资源。所以jvm在内存的堆区域内划分出一块区域,名曰“常量池”,它的作用就是如果已经有要定义的东西了,那就不新建了,直接返回引用就可以了,如下面所示,其实大家指向的是同一个空间,这么玩就节省了频繁创建销毁对象带来的开销了。
可能还有原因是为了线程安全吧,设想String不可变,不能修改这块,你都不能写了,只能并发读,当然是安全的了。
2.2 如何实现不可变
我们查看jdk源码会发现:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
……
……
}
核心是那个value是private final的,且没提供操作修改value具体里面内容的方法;String这个类本身又是final,不让继承修改。所以String定义的内存空间也就不能改了。
2.3 常见经典问题
String s = “abcd”;
s = “abcdel”; //String是不可变的,所以”abcdel”又是一块新地址
String s0 = "abcd";
String s1 = "el";
String s3 = "abcdel";
String s4 = s0 + s1;
String s5 = "abcd" + s1;
String s6 = "abcd" + "el";
System.out.println(s3 == s4); //false
System.out.println(s3 == s5); //false
System.out.println(s3 == s6); //true
上面这块程序主要是两个知识点:
- 直接定义s=”abcd”这种是放在常量池里的,new String(“ss”)是放堆里的。
- 拼接符号“+”的运算符如果有变量,那么执行的实际是StringBuilder.append,结果丢堆里面去了。如果是两个字符串直接相加,则是结果放在常量池里了。
所以上面的s3是常量池里的,s4,s5是堆里面的两个对象,s6是常量池里的,执向的就是s3.
String的不可变性虽然能实现常量池这个特性,但是如果字符串要改来改去,就要不断的申请新的空间,这样是很浪费资源的,于是乎,就改StringBuilder和StringBuffer登场了。
3. StringBuilder & StringBuffer
StringBuiler和StringBuffer类的对象都可以多次被修改,在需要频繁修改对象内容的场景下节省系统开销。
既然两者都存在,那么必然有不同,同样是append(String),
StringBuilder源码:
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
StringBuffer源码:
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
对的,重点就是StringBuffer给方法基本都加了同步锁synchronized,所以它线程安全的。
4. 结论
String是不可变的,StringBuilder和StringBuffer都是可变的,一个线程不安全,一个线程安全。
计算机现在速度这么块,感觉这些效率问题差异应该都是毫无感知的吧,怎么方便怎么写呗,但是本着探索问题,还是得知道下的:
String使用场景:定义常量字符串用来读取,不涉及修改的时候; 用来做唯一标识区别的时候,比如set统计元素,map的key值。
StringBuider, StringBuffer: 需要多次修改对象内容,比如对最后的结果进行循环拼接字符串之类的。如果要考虑线程安全,并发读写的话,那么得用StringBuffer。
5. 参考链接
几张图轻松理解String.intern()_唐大麦_csdn
在java中String类为什么要设计成final?_const伐伐_csdn
图析:String,StringBuffer与StringBuilder的区别_Chin_style_csdn
String,StringBuffer,StringBuilder的区别及其源码分析(一)_Wilange_cnblogs
String,StringBuffer,StringBuilder的区别及其源码分析(二)_Wilange_cnblogs