String类对象相加时做了什么

我们都知道java中的加号操作符除了加法、表示正数之外,还可以用作字符串的连接。初学java时,你很可能会碰到类似下面的题目:

以下这段代码产生了几个String对象:

String str1 = "abc" + "def";
String str2 = "123" + new String("456");

我还记得以前看过的文章是这样分析的:第一行先产生abc和def,再产生一个新的abcdef,共3个;第二行先产生123,再产生一个456,最后再产生一个123456,共3个;一共产生了6个String对象。

先说结论,上面的分析过程是错误的,这两段代码一共产生了5个String对象。下面是验证过程:

先编译,然后再反编译可以得到下面的代码:

String var1 = "abcdef";
String var2 = "123" + new String("456");

可以看到第一行在编译的阶段就直接把两个字符串常量的连接结果计算出来了,直接在常量池生成一个abcdef字符串,所以只产生了一个String对象;而第二行并没有进行计算,即使我们可以很直观地看出计算结果。在有new的地方,只有在运行阶段才会去动态分配内存,然后进行初始化的,所以第二行会生成4个String对象,即在常量池生成一个123和一个456对象,再在堆中生成456和123456两个String对象。

然后回到字符串相加的问题,上面两行代码都是字符串相加,分别做了什么呢?可以用javap -c反汇编查看指令如下:

0: ldc           #2                  // String abcdef
2: astore_1
3: new           #3                  // class java/lang/StringBuilder
6: dup
7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
10: ldc           #5                  // String 123
12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: new           #7                  // class java/lang/String
18: dup
19: ldc           #8                  // String 456
21: invokespecial #9                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
24: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
30: astore_2
31: return

可以看到第一行是在编译阶段就把两个常量字符串相加好了直接生成了字符串常量;第二行代码做的工作比较多,先是生成了一个StringBuilder对象,然后append了123,接着生成一个String对象,值为456,接着再被append到StringBuilder中,最后再调用toString方法得到了最终的字符串。

再看一个例子,下面的代码做了怎样的操作?

String str1 = 1 + 2 + "345";
String str2 = 1 + 2 + new String("345");

用同样的方法,用javap命令对class文件反汇编查看指令:

0: ldc           #2                  // String 3345
2: astore_1
3: new           #3                  // class java/lang/StringBuilder
6: dup
7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
10: iconst_3
11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
14: new           #6                  // class java/lang/String
17: dup
18: ldc           #7                  // String 345
20: invokespecial #8                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
23: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
26: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: astore_2
30: return

第一行依然是在编译阶段就完成了计算;第二行还是使用了StringBuilder对象,先在编译阶段计算了1+2的值,然后再用append方法拼接后toString获得结果。

posted @ 2020-06-24 23:14  我是满意吖  阅读(546)  评论(0编辑  收藏  举报