字符拼接的深入理解

字符串拼接在日常中编码中大量使用,但是对底层的原理却缺乏理解。
我们还是从一段代码开始:

public static void main(String[] args) throws Exception{
        String s1="a";
        String s2="b";
        String s3="ab";
        String s4=s1+s2;
        System.out.println(s3==s4);
    }

运行输出:

false

我们反编译字节码看看:

javap -v Jvm1_22

选取关键部分:

        0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         8: astore_3
         9: new           #5                  // class java/lang/StringBuilder
        12: dup //复制一份对象引用到栈顶
        13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V 执行构造方法
        16: aload_1
        17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        20: aload_2
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        27: astore        4
        29: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;

我们解读如下:
前面字符串"a" “b” "ab"的生成没啥问题,我们关注 String s4=s1+s2的执行过程,
从第9-13行,我们看到new 关键字,其实是创建了StringBuilder对象
16行加载了第一个槽位中的数a
17行调用了append的操作
10行加载了第二个槽位中的数,21行调用了append的操作
24行调用了tostring的方法,27行则把对象进行保存。
我们再进一步看StringBuilder中的toString方法:

 @Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }

可以很清楚看到,这里会利用拼接好的值new一个新的String对象,所以我们最终拼接的结果会是一个新对象,这就是a+b的生成过程,可以很好解释s3==s4结果是false。

总结

字符串的+ 拼接操作其实底层会调用StringBuilder拼接一个新的字符串对象出来,有new的操作,返回的是一个新的对象。

进一步理解

我们看看另一种情况:

 public static void main(String[] args) throws Exception{
        String s1="a";//用到了这个对象,才开始创建这个对象,行为上面是懒惰的
        String s2="b";
        String s3="ab";

        String s4=s1+s2;
        System.out.println(s3==s4);
        String s5="a"+"b"; 
        System.out.println(s5==s3);
    }

反编译结果如下:

  32: aload_3
        33: aload         4
        35: if_acmpne     42
        38: iconst_1
        39: goto          43
        42: iconst_0
        43: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        46: ldc           #4                  // String ab
        48: astore        5
        50: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
        53: aload         5

我们看到46行,字符串直接就取了ab的值,自然输出结果就是true。
这里的解释是javac在编译期的优化,它认为"a" “b"都是常量,拼接的结果在编译期间就确定为"ab”,不可能再变回了,上一行代码是引用的值,有可能发现修改。

发布了48 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/zhuxuemin1991/article/details/103938136