StringBuilder生产使用的一次事故
使用Java实现较长逻辑的代码中,无可避免会创建众多的String对象,又是为了节省内存空间以及优化程序效率,会选择使用StringBuilder或者StringBuffer来代替String对象。
起因
项目部署后,NPE的次数变多了,且均在StringBuilder构造器中,这引起的项目维护人的重视!
验证
代码语言:javascript复制public class StringBuilderTest {
public static void main(String[] args) {
StringBuilder builder = new StringBuilder(null);
}
}
执行结果如下:
到了这一步,基本算是复现了线上问题,确实是编码问题?那么为什么这样呢,明明append可以放入空指针对象呀?
深入源码查看,进入StringBuilder.java
的带参构造器
/**
* 构造初始化为指定字符串内容的字符串生成器。字符串生成器的初始容量为 16 加上字符串参数的长度。
* 参数:str – 缓冲区的初始内容。
*/
public StringBuilder(String str) {
super(str.length() 16);
append(str);
}
到这里算是明白了,调用length()
方法,所以会有空指针的异常,瞬间感觉这个错误很低级。
解决方案
基于以上问题,基本解决方案就是,避免传入NULL
对象,或者使用append()
。
方法一:
对于使用StringBuilder构造器的对象,进行非空判断,例如:
代码语言:javascript复制public static void main(String[] args) {
String obj = null;
if (obj != null) {
StringBuilder builder = new StringBuilder(obj);
}
}
方法二:
代码语言:javascript复制public static void main(String[] args) {
String obj = null;
StringBuilder builder = new StringBuilder();
builder.append(obj);
}
由于append在添加字符串时,会先进行判空并 替代塞入"null",所以这个方法不提倡。
那么它的源码是如何实现的呢?
StringBuilder.java
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
AbstractStringBuilder.java
/**
* 将指定的字符串追加到此字符序列。将按顺序追加 String 参数的字符,使此序列的长度按参数的长度增加。
* 如 果 str 为 null,则附加四个字符 “null”。设 n 是执行追加方法之前此字符序列的长度。则新字符序
* 列中索引 k 处的字符等于旧字符序列中索引 k 处的字符,如果 k 小于 n;否则,它等于参数 str 中索引
* k-n 处的字符。
* 参数:str – 一个字符串。
* 返回:对此对象的引用。
**/
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count len);
str.getChars(0, len, value, count);
count = len;
return this;
}
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c 4);
final char[] value = this.value;
value[c ] = 'n';
value[c ] = 'u';
value[c ] = 'l';
value[c ] = 'l';
count = c;
return this;
}
以上就是一次事故给自己的一些思考,优化可以,但是不可以多写bug。