最近开始上班,路上时间变长为1小时,目前还没找到路上可以干点啥。于是就刷刷公众号文章,今天早上一不小心“偷看”了老王(公众号:Java中文社区)的一篇关于String性能提升方法分析的文章。引发了一场底层代码实现的探索。

老王的文章中关于字符串的处理和效率提升都没错,但在总结中提到“不要直接+=字符串”的建议却引起我的怀疑。在路上就进行了评论:

“+=操作记得在最新版本是分场景的,比如正常来说没有使用for循环这种情况,jdk默认会转换StringBuilder来进行操作,而for循环内它会在for循环内不停的创建StringBuilder,所以才需要在外面声明。”

老王的回复是:我看很多资料也是这样说,但jdk8和jdk11我反编译都没发现这种情况。

原本结论和建议是没错的,但技术人有时候就爱较个真。于是到公司的第一件事就是写了两个程序来验证。

第一个程序:

public class TestString {
    public static void main(String[] args) {
        String a = "a";
        String b = a + "b";
        System.out.println(b);
    }
}

先尝试了反编译,发现反编译之后与源代码一样,也就是说通过反编译看不出来差别。那就查看字节码。

执行:

javap -c TestString.class
image

显示结果:

通过字节码看到,的确是被优化,创建了StringBuilder,然后再进行字符串拼接。随后又验证一下for循环的情况,是不是每次在for循环内部都会新创建一个StringBuilder。

public class TestString {
    public static void main(String[] args) {
        String a = "a";
        for(int i=0; i< 3; i++){
            a += ("a" + i);
        }
        System.out.println(a);
    }
}
image

查看对应字节码如下:

这里在for循环中虽然没有重复创建StringBuilder,但还是默认将String字符串转换为StringBuilder进行处理了。以上验证采用的是Oracle官方jdk8进行测试的。

于是,开始给老王发信息,进行“挑战”。不一会儿老王也发来回复消息,说:为什么我都不是这样呢?并附带了截图。

image

一看他的截图,还真是不一样。于是开始一起比对环境,后来才发现他使用的是如下版本:

✗ java -version
'openjdk version "14" 2020-03-17
OpenJDK Runtime Environment (build 14+36-1461)
OpenJDK 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)

于是,一场争辩结束,随之大家共同发现一件事。网络上流传的JDK8默认帮忙优化String字符串拼接为StringBuilder在openjdk的分支上并不成立,而在Oracle提供的JDK版本中才成立,而且要JDK8及以上版本。

其实经过几轮的讨论和各种尝试,发现网络上的一句话成立可能是有很多先决条件的,真是“理儿越变越明”。同时,技术也只有在这种碰撞中才能快速提升。

今天文章为大家分享了一个技术点,学到了便是赚了。但如果你能通过整个事件的过程总结一套学习如何从分歧中获得新知,如果从疑问中实践出新知,那么你真的是赚大发了。



偷看了隔壁老王的文章,发现String拼接另有天地插图3

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:https://choupangxia.com/2020/04/29/string/