(源码查看记录 一) String 与 StringBuffer 与 StringBuilder
“他们三者之间的区别”
这个问题已经被问烦了,问题的答案也在网上到处可以搜索到,简单概括一下:
- 1 对象的创建
String对象的创建和赋值必然会新建一个String对象,字符串的拼接也遵守这个规则;
StringBulider和StringBuffer只会创建一个对象,最后通过toString方法输出。
原因:在class文件被JVM装载到内存中时,JVM会创建一块缓冲池,用来存放代码中的字符串本身,而声明的变量只是一个对字符串本身的引用对象,如果对同一个声明的字符串变量进行赋值,则这个变量的引用就会发生改变,就会创建创建出新的引用对象。所以,String的拼接等操作,即使声明的变量名不改变,但实际上他对应的对象已经是一个新的对象。顺带一提,在JVM将字符串加载到缓冲池中后,后面的字符串对象会先去找已经存在的字符串,如果存在直接引用,若不存在则将其添加在缓冲池中。
- 2 线程安全
StringBuffer线程安全,String、StringBulider非线程安全。
原因:线程是否安全决定于此变量是否允许同步,通过以下源码可以一目了然:
//StringBuffer.java
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
//StringBulider.java
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
- 3 执行速度
StringBulider > StringBuffer > String
原因:String的拼接等操作一定涉及了众多对象的创建,所以最慢;StringBuffer不允许同步操作,势必存在线程等待,所以第二慢。
- 4 结论
上述原因都建立在大量字符串操作的条件上,只是new 一个字符串完全没必要考虑这么多。局部变量基本不用考虑线程安全的问题,所以尽量使用StrngBulider。
源码总结(仅作为阅读笔记,内容可能存在错漏)
主要侧重在 StringBuffer 与 StringBulider 上。
1 父类及接口
共同继承父类:AbstractStringBuilder.java;
共同实现接口:java.io.Serializable 、 CharSequence 。
java.io.Serializable 为标记序列化的接口,不做阐述。
2 接口CharSequence
2.1 CharSequence subSequence(int start, int end);
里面定义了字符串常用的一些方法,其中的 CharSequence subSequence(int start, int end) 方法引起我的注意。这个方法本身是用来做字符串截取的,与 subString() 方法是同样的效果,这是返回类型不一样。趁此机会看一下源码的实现。
//AbstractStringBuilder.java
char[] value;
@Override
public CharSequence subSequence(int start, int end) {
return substring(start, end);
}
public String substring(int start, int end) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
throw new StringIndexOutOfBoundsException(end);
if (start > end)
throw new StringIndexOutOfBoundsException(end - start);
return new String(value, start, end - start);
}
//String.java
public CharSequence subSequence(int beginIndex, int endIndex) {
return this.substring(beginIndex, endIndex);
}
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
if (offset <= value.length) {
this.value = "".value;
return;
}
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
**结论 😗*subSequence方法只是调用了subString方法,返回类型不同而已。
2.2 IntStream codePoints();
codePointAt的返回值是Unicode码构成的返回值。
3 其他
StringBulider 和 StringBuffer 的容量有16字节的缓冲
//StringBulider.java
public StringBuilder() {
super(16);
}
public StringBuilder(int capacity) {
super(capacity);
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
//StringBuffer.java
public StringBuffer() {
super(16);
}
public StringBuffer(int capacity) {
super(capacity);
}
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}