深度剖析‘StringBuilder‘ can be replaced with ‘String‘ 提示

1 篇文章 0 订阅

一、背景
在写代码的时候使用 StringBuilder 进行字符串拼接时,IDEA 很可能会给出下面的提示: ‘StringBuilder’ can be replaced with ‘String’ 。

那么为什么会给出这种提示?这种提示意味着什么?

二、思考
之前有讲过:“每一个疑问背后都隐藏着至少一个盲点和学习的绝佳机会”。因此我们不会轻易放过这个机会。

另外很多人这个时候可能就要开始百度了!

停!!

“《阿里巴巴Java开发手册》详解” 专栏里有讲到“先猜想后验证”的思想,对学习源码等有很大帮助,这样印象会更深刻。

根据提示:‘StringBuilder’ can be replaced with ‘String’ ,说明这种情况下可以将 StringBuilder 替换成 String。

我们知道之所以用 StringBuilder 是为了避免字符串拼接过程中产生很多不必要的字符串对象。

那么既然可以替换为 String ,说明这种情况下两者可能等价,也就是说底层是一样的。

三、分析
“《阿里巴巴Java开发手册》详解” 专栏里有讲过写 DEMO ,反编译和反汇编是学习的一个重要手段。

那咱们直接写个 DEMO验证下吧:

public class StringDemo {

public static void main(String[] args) {
    String a = "a";
    Integer b = 0;
    Long c = 1000L;
    useStringBuilder(a, b, c);
    usePlus(a, b, c);
}

/**
 * 使用 StringBuilder 拼接
 */
private static void useStringBuilder(String a, Integer b, Long c) {
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append(a).append(b).append(c);
    System.out.println(stringBuilder.toString());
}

/**
 * 直接使用+号进行字符串拼接
 */
private static void usePlus(String a, Integer b, Long c) {
    System.out.println(a + b + c);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
使用 IDEA 自带的反编译工具,发现和源代码几乎相同(这里就不给出了)。

接下来尝试反汇编 javap -c -p StringDemo(之前已经通过 javac 进行了编译):

public class com.chujianyun.common.str.StringDemo {
public com.chujianyun.common.str.StringDemo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object.""😦)V
4: return

public static void main(java.lang.String[]);
Code:
0: ldc #2 // String a
2: astore_1
3: iconst_0
4: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
7: astore_2
8: ldc2_w #4 // long 1000l
11: invokestatic #6 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
14: astore_3
15: aload_1
16: aload_2
17: aload_3
18: invokestatic #7 // Method useStringBuilder:(Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Long;)V
21: aload_1
22: aload_2
23: aload_3
24: invokestatic #8 // Method usePlus:(Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Long;)V
27: return

private static void useStringBuilder(java.lang.String, java.lang.Integer, java.lang.Long);
Code:
0: new #9 // class java/lang/StringBuilder
3: dup
4: invokespecial #10 // Method java/lang/StringBuilder.""😦)V
7: astore_3
8: aload_3
9: aload_0
10: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
13: aload_1
14: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
17: aload_2
18: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
21: pop
22: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
25: aload_3
26: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
32: return

private static void usePlus(java.lang.String, java.lang.Integer, java.lang.Long);
Code:
0: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #9 // class java/lang/StringBuilder
6: dup
7: invokespecial #10 // Method java/lang/StringBuilder.""😦)V
10: aload_0
11: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: aload_1
15: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
18: aload_2
19: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
22: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
25: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
28: return
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
注:大家也可以使用专栏第一个加餐文章中推荐的 jclasslib 字节码插件插件来查看字节码

大家可以对比下 usePlus 函数和 useStringBuilder 函数的字节码指令会发现带变量的 + 号拼接方式最终将转化为 StringBuilder 进行字符串拼接。

相当于是编译器的优化,因此 IDEA 检测到两者等价,为了让代码更加简洁,所以给出了上述提示。
为了再次印证这个想法我们打开《Java 语言规范》15.18.1. String Concatenation Operator +:

An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.

Java 语言实现可以选择在某个步骤中执行转换和拼接,以避免先创建然后又丢弃中间的字符串对象。为了提高字符串连接操作的性能,Java 编译器可以使用功能 StringBuilder 类或者类似的技术来减少计算表达式时创建的中间 String 对象的数量。

因此问题就彻底清楚了。

四、总结
之前有写过《为什么要推荐大家学习字节码》和《每一个疑问背后都隐藏着至少一个盲点和学习的绝佳机会》两篇文章。希望大家能够真正理解学习字节码的用处;能够真真抓住每一次疑问,通过对每一个疑问的深入探究来彻底掌握某个知识。另外强烈推荐大家多看《Java 语言规范》和《Java 虚拟规范》,它们更全面且更权威。

希望本文能对大家有帮助。如果有任何问题想和我交流,欢迎留言评论。

如果我的文章对你有帮助,欢迎关注,点赞评论!!
————————————————
版权声明:本文为CSDN博主「明明如月学长」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/w605283073/article/details/104327178

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值