1、线程问题
StringBuffer和StringBuilder
这两者都是继承AbstractStringBuilder,区别就是:StringBuffer线程安全,里面所有的方法都有synchronized关键字
//StringBuffer源码
……
public synchronized int length() {
return count;
}
public synchronized int capacity() {
return value.length;
}
public synchronized void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > value.length) {
expandCapacity(minimumCapacity);
}
}
……
2、字符串连接:
对 String 对象可以使用concat方法 和 + 进行字符串的连接。
这两个在大部分情况下是一样的,但在一种情况下也就是连接一个null对象时结果是不同的。
使用concat连接null,会抛出异常。源码如下:
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
不清楚为什么这个地方没有关于null的判断。
String是不可变的,所以每次concat之后,都会是新建一个String对象。
“+” 连接null对象,会将null作为字符串“null”处理。
其实通过反编译可以看到 “+”的内部实现是StringBuilder的append方法。
StringBuilder的源码:
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
“+”和StringBuffer/StringBuilder 的append方法实现一样的。
当使用“+”的时候,就会默认新建一个StringBuilder对象,
如果只有一句 String str = “a” +b; 这样子的,其效率与
String str = new StringBuilder().append(“a”).append(b).toString();
是一样的。
当使用循环+的时候,使用append效率会高。
String a ="a";
for(int i=0;i<100;i++){
a=a+"b";
}
因为每次+都会新建一个StringBuilder,然后丢掉,再新建。
而是用append则只会新建一个StringBuilder对象,可以减少StringBuilder对象的创建,但是并不会一定就不产生新的垃圾,在字符串过长的情况下,会产生char数组垃圾。
因为在append的时候如果原来的StringBuilder的char数组的长度,默认是16,已经不够用,则会新建一个char数组对象。
//第一步 扩展
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
//重要 扩容
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
//扩容实现
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code 超过时扩充
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
/**
* This implements the expansion semantics of ensureCapacity with no
* size check or synchronization.
*/
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
// 字符串复制
value = Arrays.copyOf(value, newCapacity);
}
//字符串复制实现
public static char[] copyOf(char[] original, int newLength) {
//新的char数组产生
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
具体内容可看我的博客:
Java 中字符串增长和redis中字符串增长的实现对比