说明:下例中,反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行 append 操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。
反例:
循环中的字符串连接。当每个String串联复制整个String时,通常最好将其替换为对StringBuilder.append()或StringBuffer.append()的显式调用。
-
修正

- StringBuffer.append()
- StringBuilder.append()或
- Appendable.append()
的参数的字符串串联。这样的调用可以有益地转换为对现有StringBuffer / Builder / Appendable的链接追加调用,从而节省了额外的StringBuffer / Builder分配的成本。 此检查将忽略编译时评估的String串联,将其转换为链接的append调用只会降低性能
-
改成链式调用
StringBuilder 的内容被更新,但从未被查询过 检查信息:已读取但未写入或已写入但未读取的StringBuilder或StringBuffer字段或变量。 这种不匹配的读写是没有意义的,并且可能表示死的、不完整的或错误的代码。
-
修正,让该字符串被读起来!
stringbuffer固然是线程安全的,固然是比stringbuilder更慢,固然,在多线程的情况下,理论上是应该使用线程安全的stringbuffer的。
然而有谁给我一个实际的案例来显示你需要一个线程安全的string拼接器?
适用场景最简单的回答是,stringbuffer基本没有适用场景,你应该在所有的情况下选择使用stringbuilder
即使你真的遇到了这样的场景,很不幸的是,恐怕你仍然有99.99…99%的情况下没有必要选择stringbuffer,因为stringbuffer的线程安全,仅仅是保证jvm不抛出异常顺利的往下执行而已,它可不保证逻辑正确和调用顺序正确。大多数时候,我们需要的不仅仅是线程安全,而是锁。
为什么会有stringbuffer如果真的没有价值,为什么jdk会提供这个类? 答案太简单了,因为最早是没有stringbuilder的,sun的人不知处于何种愚蠢的考虑,决定让stringbuffer是线程安全的,然后大约10年之后,人们终于意识到这是一个多么愚蠢的决定,意识到在这10年之中这个愚蠢的决定为java运行速度慢这样的流言贡献了多大的力量
于是,在jdk1.5的时候,终于决定提供一个非线程安全的stringbuffer实现,并命名为stringbuilder。顺便,javac好像大概也是从这个版本开始,把所有用加号连接的string运算都隐式的改写成stringbuilder,也就是说,从jdk1.5开始,用加号拼接字符串已经没有任何性能损失了。
使用String类的场景在字符串不经常变化的场景中可以使用String类,例如常量的声明、少量的变量运算
使用StringBuffer类的场景在频繁进行字符串运算(如拼接、替换、删除等),并且运行在多线程环境中,则可以考虑使用StringBuffer,例如XML解析、HTTP参数解析和封装
使用StringBuilder类的场景在频繁进行字符串运算(如拼接、替换、和删除等),并且运行在单线程的环境中,则可以考虑使用StringBuilder,如SQL语句的拼装、JSON封装等。
扩容相关
没有传参的情况下默认初始容量是16。
有参情况下,初始容量是16+字符串的长度,用append方法追加字符。
那这个字符串的长度是多少呢。是它本身的长度还是16+它自身的长度?
一路追寻append()方法,len是String自身的长度。其实平时咱们也在用str.length()。
要是在追加字符串的时候长度比16大怎么办,我们看到有个ensureCapacityInternal方法
扩容:
- 增加为自身长度的一倍然后再加2
- 若还是放不下,直接扩容到它需要的长度newCapacity = minCapacity;
参考
- https://www.zhihu.com/question/20101840/answer/18901280