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

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

一、背景

在写代码的时候使用 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);
    }

}

使用 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."<init>":()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."<init>":()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."<init>":()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
}

注:大家也可以使用专栏第一个加餐文章中推荐的 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 虚拟规范》,它们更全面且更权威。

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

如果我的文章对你有帮助,欢迎关注,点赞评论!!

在这里插入图片描述

相关文章
|
4月前
|
安全 Java API
【Java字符串操作秘籍】StringBuffer与StringBuilder的终极对决!
【8月更文挑战第25天】在Java中处理字符串时,经常需要修改字符串,但由于`String`对象的不可变性,频繁修改会导致内存浪费和性能下降。为此,Java提供了`StringBuffer`和`StringBuilder`两个类来操作可变字符串序列。`StringBuffer`是线程安全的,适用于多线程环境,但性能略低;`StringBuilder`非线程安全,但在单线程环境中性能更优。两者基本用法相似,通过`append`等方法构建和修改字符串。
74 1
|
20天前
|
安全
String、StringBuffer、StringBuilder的区别
String 由 char[] 数组构成,使用了 final 修饰,对 String 进行改变时每次都会新生成一个 String 对象,然后把指针指向新的引用对象。 StringBuffer可变并且线程安全;有一定缓冲区容量,字符串大小没超过容量,不会重新分配新的容量,适合多线程操作字符串; StringBuiler可变并且线程不安全。速度比StringBuffer更快,适合单线程操作字符串。 操作少量字符数据用 String;单线程操作大量数据用 StringBuilder;多线程操作大量数据用 StringBuffer
|
3月前
|
安全 Java
String、StringBuffer、StringBuilder的区别
这篇文章讨论了Java中String、StringBuffer和StringBuilder的区别。String是不可变的,每次操作都会产生新的对象,效率低且浪费内存。StringBuilder可以在原字符串基础上进行操作,不开辟额外内存,弥补了String的缺陷。StringBuffer和StringBuilder类似,但StringBuffer的方法是线程安全的。文章还列举了StringBuffer的常用方法,并提供了使用示例代码。最后总结了这三者的主要区别。
String、StringBuffer、StringBuilder的区别
|
2月前
|
canal 安全 索引
(StringBuffer和StringBuilder)以及回文串,字符串经典习题
(StringBuffer和StringBuilder)以及回文串,字符串经典习题
38 5
|
2月前
|
存储 安全 Java
String、StringBuffer 和 StringBuilder 的区别
【10月更文挑战第21天】String、StringBuffer 和 StringBuilder 都有各自的特点和适用场景。了解它们之间的区别,可以帮助我们在编程中更合理地选择和使用这些类,从而提高程序的性能和质量。还可以结合具体的代码示例和实际应用场景,进一步深入分析它们的性能差异和使用技巧,使对它们的理解更加全面和深入。
31 0
|
3月前
|
安全 Java
Java StringBuffer 和 StringBuilder 类详解
在 Java 中,`StringBuffer` 和 `StringBuilder` 用于操作可变字符串,支持拼接、插入、删除等功能。两者的主要区别在于线程安全性和性能:`StringBuffer` 线程安全但较慢,适用于多线程环境;`StringBuilder` 非线程安全但更快,适合单线程环境。选择合适的类取决于具体的应用场景和性能需求。通常,在不需要线程安全的情况下,推荐使用 `StringBuilder` 以获得更好的性能。
48 8
|
4月前
|
安全 Java
【Java基础面试二十七】、说一说StringBuffer和StringBuilder有什么区别
这篇文章介绍了Java中StringBuffer和StringBuilder的区别:StringBuffer是线程安全的,而StringBuilder是非线程安全的,因此在单线程环境下优先推荐使用StringBuilder以获得更好的性能。
|
4月前
|
安全 Java API
Java系类 之 String、StringBuffer和StringBuilder类的区别
这篇文章讨论了Java中`String`、`StringBuffer`和`StringBuilder`三个类的区别,其中`String`是不可变的,而`StringBuffer`是线程安全的可变字符串类,`StringBuilder`是非线程安全的可变字符串类,通常在单线程环境下性能更优。
Java系类 之 String、StringBuffer和StringBuilder类的区别
|
4月前
|
API C# 开发者
WPF图形绘制大师指南:GDI+与Direct2D完美融合,带你玩转高性能图形处理秘籍!
【8月更文挑战第31天】GDI+与Direct2D的结合为WPF图形绘制提供了强大的工具集。通过合理地使用这两种技术,开发者可以创造出性能优异且视觉效果丰富的WPF应用程序。在实际应用中,开发者应根据项目需求和技术背景,权衡利弊,选择最合适的技术方案。
177 0
|
4月前
|
缓存 安全 Java
【揭秘】String vs StringBuilder vs StringBuffer:三大字符串类的秘密较量!你真的知道何时该用哪个吗?
【8月更文挑战第19天】探讨Java中`String`、`StringBuilder`与`StringBuffer`的区别及应用场景。`String`不可变,适合做哈希表键或多线程共享。`StringBuilder`支持动态修改字符串,适用于单线程环境以提高性能。`StringBuffer`与`StringBuilder`功能相似,但线程安全。示例代码展示各类型的基本用法。选择哪种类型取决于具体需求和性能考量。
59 0