2021-05-04 19:36  阅读(2157)
文章分类:从 JDK 源码看 Java 文章标签:JavaJDK 源码
©  原文作者:超人汪小建(seaboat) 原文地址:https://blog.csdn.net/wangyangzhizhou/column/info/16032

前言

据我所知字符串确实已经成为 Java 开发人员最常用的类了,而且是大量使用。我们都知道,String 其实是封装了字符,所以俩字符串连接就是将字符串对象里面的字符连起来。很多人习惯使用+来连接字符串,也有人会用 StringBuilder 的append方法。

“+”编译后

看看如果我们在程序中直接使用+来连接字符串的情况,用下面一个简单的例子来说明,进行两个字符串连接操作,即s3 = s1 + s2

        public class TestString {
            public static void main(String[] args) {
                String s1 = "www";
                String s2 = "ccc";
                String s3 = s1 + s2;
            }
        }
    

接着javap -c TestString.class看一下编译后的情况,可以看到编译器其实是对+进行了转换的,转成了 StringBuilder 对象来操作了,首先使用 s1 创建 StringBuilder 对象,然后用 append方法连接 s2,最后调用toString方法完成。

        public class com.seaboat.string.TestString {
          public com.seaboat.string.TestString();
            Code:
               0: aload_0
               1: invokespecial #8                  // Method java/lang/Object."<init>":()V
               4: return
    
          public static void main(java.lang.String[]);
            Code:
               0: ldc           #16                 // String www
               2: astore_1
               3: ldc           #18                 // String ccc
               5: astore_2
               6: new           #20                 // class java/lang/StringBuilder
               9: dup
              10: aload_1
              11: invokestatic  #22                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
              14: invokespecial #28                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
              17: aload_2
              18: invokevirtual #31                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
              21: invokevirtual #35                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
              24: astore_3
              25: return
        }
    

“+”与”append”等价吗

前面可以看到+在编译器作用下都会转成 StringBuilder 的append方法执行,所以如果抛开运行效率来说,它们其实本质是一样的。

本质一样是否就能说明它们时等价的呢?或者说能否为了方便直接用+来连接字符串,剩下的事就交给编译器了?继续看个例子,在这个例子中有个 for 循环进行字符串连接操作。

        public class TestString2 {
            public static void main(String[] args) {
                String s = "www";
                for (int i = 0; i < 10; i++)
                    s += i;
            }
        }
    

编译后的情况如下,不熟悉指令没关系,我们只看重要的部分,if_icmplt 8,这个就是 for 循环的条件判断,小于10则不断跳到8的位置,8后面其实就是创建 StringBuilder 对象,并以本地变量s的值初始化该对象,接着再将本地变量i append到 StringBuilder 对象中,最后调用toString方法将所得值存到本地变量s。

这样来看循环中每次都要创建 StringBuilder 对象,而且要调用toString方法,这样的执行效率显然比较低,而且增加了 GC 的压力。

        public class com.seaboat.string.TestString2 {
          public com.seaboat.string.TestString2();
            Code:
               0: aload_0
               1: invokespecial #8                  // Method java/lang/Object."<init>":()V
               4: return
    
          public static void main(java.lang.String[]);
            Code:
               0: ldc           #16                 // String www
               2: astore_1
               3: iconst_0
               4: istore_2
               5: goto          30
               8: new           #18                 // class java/lang/StringBuilder
              11: dup
              12: aload_1
              13: invokestatic  #20                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
              16: invokespecial #26                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
              19: iload_2
              20: invokevirtual #29                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
              23: invokevirtual #33                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
              26: astore_1
              27: iinc          2, 1
              30: iload_2
              31: bipush        10
              33: if_icmplt     8
              36: return
        }
    

友好写法

把事情都丢给编译器是不友好的,为了能让程序执行更加高效,最好是我们自己来控制 StringBuilder 的实例,比如下面,只创建一个 StringBuilder 实例,后面用append方法连接字符串。

        public class TestString3 {
            public static void main(String[] args) {
                StringBuilder sb = new StringBuilder("www");
                for (int i = 0; i < 10; i++)
                    sb.append(i);
            }
        }
    

编译后的情况如下,首先创建一个 StringBuilder 对象,使用字符串”www”来实例化该对象,接着循环调用append方法将本地变量i添加到 StringBuilder 对象中。

        public class com.seaboat.string.TestString3 {
          public com.seaboat.string.TestString3();
            Code:
               0: aload_0
               1: invokespecial #8                  // Method java/lang/Object."<init>":()V
               4: return
    
          public static void main(java.lang.String[]);
            Code:
               0: new           #16                 // class java/lang/StringBuilder
               3: dup
               4: ldc           #18                 // String www
               6: invokespecial #20                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
               9: astore_1
              10: iconst_0
              11: istore_2
              12: goto          24
              15: aload_1
              16: iload_2
              17: invokevirtual #23                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
              20: pop
              21: iinc          2, 1
              24: iload_2
              25: bipush        10
              27: if_icmplt     15
              30: return
        }
    
点赞(2)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> 字符串连接你用+还是用StringBuilder
上一篇
从JDK源码看StringBuilder
下一篇
《字符串连接你用+还是用StringBuilder》续