> 文章列表 > StringBuilder生产使用的一次事故

StringBuilder生产使用的一次事故

StringBuilder生产使用的一次事故

StringBuilder生产使用的一次事故

使用Java实现较长逻辑的代码中,无可避免会创建众多的String对象,又是为了节省内存空间以及优化程序效率,会选择使用StringBuilder或者StringBuffer来代替String对象。

起因

项目部署后,NPE的次数变多了,且均在StringBuilder构造器中,这引起的项目维护人的重视!

验证

public class StringBuilderTest {public static void main(String[] args) {StringBuilder builder = new StringBuilder(null);}
}

执行结果如下:
StringBuilder生产使用的一次事故
到了这一步,基本算是复现了线上问题,确实是编码问题?那么为什么这样呢,明明append可以放入空指针对象呀?
深入源码查看,进入StringBuilder.java的带参构造器

    /* 构造初始化为指定字符串内容的字符串生成器。字符串生成器的初始容量为 16 加上字符串参数的长度。* 参数:str – 缓冲区的初始内容。*/public StringBuilder(String str) {super(str.length() + 16);append(str);}

到这里算是明白了,调用length()方法,所以会有空指针的异常,瞬间感觉这个错误很低级。

解决方案

基于以上问题,基本解决方案就是,避免传入NULL对象,或者使用append()

方法一:

对于使用StringBuilder构造器的对象,进行非空判断,例如:

public static void main(String[] args) {String obj = null;if (obj != null) {StringBuilder builder = new StringBuilder(obj);}
}
方法二:
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。