探索字节码底层String和StringBuilder的区别

探索字节码底层String和StringBuilder的区别

1.查看字节码文件

public class Test1 {
	public static void main(String[] args) {
		new Test1().m1();
		new Test1).m2();
	}
	public void m1(){
		String s1 = "123";
		String s2 = "456";
		String s3 = s1 + s2;
		System.out.println(s3);
	}
	public void m2(){
		String s1 = "123";
		String s2 = "456";
		StringBuilder sb = new StringBuilder();
		sb.append(s1);
		sb.append(s2);
		String s3 = sb.toString();
		System.out.println(s3);
	}
}

字节码文件

Classfile /C:/Users/Administrator/Desktop/Test1.class
  Last modified 2019-4-17; size 806 bytes
  MD5 checksum 7d374b4c795683dafa653ffd290ee098
  Compiled from "Test1.java"
public class Test1
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #14.#25        // java/lang/Object."<init>":()V
   #2 = Class              #26            // Test1
   #3 = Methodref          #2.#25         // Test1."<init>":()V
   #4 = Methodref          #2.#27         // Test1.m1:()V
   #5 = Methodref          #2.#28         // Test1.m2:()V
   #6 = String             #29            // 123
   #7 = String             #30            // 456
   #8 = Class              #31            // java/lang/StringBuilder
   #9 = Methodref          #8.#25         // java/lang/StringBuilder."<init>":()V
  #10 = Methodref          #8.#32         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #11 = Methodref          #8.#33         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #12 = Fieldref           #34.#35        // java/lang/System.out:Ljava/io/PrintStream;
  #13 = Methodref          #36.#37        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #14 = Class              #38            // java/lang/Object
  #15 = Utf8               <init>
  #16 = Utf8               ()V
  #17 = Utf8               Code
  #18 = Utf8               LineNumberTable
  #19 = Utf8               main
  #20 = Utf8               ([Ljava/lang/String;)V
  #21 = Utf8               m1
  #22 = Utf8               m2
  #23 = Utf8               SourceFile
  #24 = Utf8               Test1.java
  #25 = NameAndType        #15:#16        // "<init>":()V
  #26 = Utf8               Test1
  #27 = NameAndType        #21:#16        // m1:()V
  #28 = NameAndType        #22:#16        // m2:()V
  #29 = Utf8               123
  #30 = Utf8               456
  #31 = Utf8               java/lang/StringBuilder
  #32 = NameAndType        #39:#40        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #33 = NameAndType        #41:#42        // toString:()Ljava/lang/String;
  #34 = Class              #43            // java/lang/System
  #35 = NameAndType        #44:#45        // out:Ljava/io/PrintStream;
  #36 = Class              #46            // java/io/PrintStream
  #37 = NameAndType        #47:#48        // println:(Ljava/lang/String;)V
  #38 = Utf8               java/lang/Object
  #39 = Utf8               append
  #40 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #41 = Utf8               toString
  #42 = Utf8               ()Ljava/lang/String;
  #43 = Utf8               java/lang/System
  #44 = Utf8               out
  #45 = Utf8               Ljava/io/PrintStream;
  #46 = Utf8               java/io/PrintStream
  #47 = Utf8               println
  #48 = Utf8               (Ljava/lang/String;)V
{
  public Test1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: new           #2                  // class Test1
         3: dup
         4: invokespecial #3                  // Method "<init>":()V
         7: invokevirtual #4                  // Method m1:()V
        10: new           #2                  // class Test1
        13: dup
        14: invokespecial #3                  // Method "<init>":()V
        17: invokevirtual #5                  // Method m2:()V
        20: return
      LineNumberTable:
        line 3: 0
        line 4: 10
        line 5: 20

  public void m1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=1
         0: ldc           #6                  // String 123
         2: astore_1
         3: ldc           #7                  // String 456
         5: astore_2
         6: new           #8                  // class java/lang/StringBuilder
         9: dup
        10: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
        13: aload_1
        14: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        17: aload_2
        18: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        21: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        24: astore_3
        25: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
        28: aload_3
        29: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        32: return
      LineNumberTable:
        line 7: 0
        line 8: 3
        line 9: 6
        line 10: 25
        line 11: 32

  public void m2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=5, args_size=1
         0: ldc           #6                  // String 123
         2: astore_1
         3: ldc           #7                  // String 456
         5: astore_2
         6: new           #8                  // class java/lang/StringBuilder
         9: dup
        10: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
        13: astore_3
        14: aload_3
        15: aload_1
        16: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: pop
        20: aload_3
        21: aload_2
        22: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        25: pop
        26: aload_3
        27: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        30: astore        4
        32: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
        35: aload         4
        37: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        40: return
      LineNumberTable:
        line 13: 0
        line 14: 3
        line 15: 6
        line 16: 14
        line 17: 20
        line 18: 26
        line 19: 32
        line 20: 40
}
SourceFile: "Test1.java"

String字节码文件
在这里插入图片描述
StringBuilder字节码文件
在这里插入图片描述
所以说两者本质是一样的

2.String和StringBuilder区别

具体区分是在两者使用方面

public class Test1 {
	public static void main(String[] args) {
		new Test1().m1();
		new Test1().m2();
	}
	public void m1(){
		String str = "";
		for (int i = 0; i < 5; i++) {
		str = str + i;
		}
		System.out.println(str);
	}
	public void m2(){
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < 5; i++) {
		sb.append(i);
		}
		System.out.println(sb.toString());
	}
}

字节码文件

Classfile /C:/Users/Administrator/Desktop/Test1.class
  Last modified 2019-4-17; size 915 bytes
  MD5 checksum d95e0f2188d6e0a7003e8942df1f27ff
  Compiled from "Test1.java"
public class Test1
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #14.#28        // java/lang/Object."<init>":()V
   #2 = Class              #29            // Test1
   #3 = Methodref          #2.#28         // Test1."<init>":()V
   #4 = Methodref          #2.#30         // Test1.m1:()V
   #5 = Methodref          #2.#31         // Test1.m2:()V
   #6 = String             #32            //
   #7 = Class              #33            // java/lang/StringBuilder
   #8 = Methodref          #7.#28         // java/lang/StringBuilder."<init>":()V
   #9 = Methodref          #7.#34         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #10 = Methodref          #7.#35         // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  #11 = Methodref          #7.#36         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #12 = Fieldref           #37.#38        // java/lang/System.out:Ljava/io/PrintStream;
  #13 = Methodref          #39.#40        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #14 = Class              #41            // java/lang/Object
  #15 = Utf8               <init>
  #16 = Utf8               ()V
  #17 = Utf8               Code
  #18 = Utf8               LineNumberTable
  #19 = Utf8               main
  #20 = Utf8               ([Ljava/lang/String;)V
  #21 = Utf8               m1
  #22 = Utf8               StackMapTable
  #23 = Class              #42            // java/lang/String
  #24 = Utf8               m2
  #25 = Class              #33            // java/lang/StringBuilder
  #26 = Utf8               SourceFile
  #27 = Utf8               Test1.java
  #28 = NameAndType        #15:#16        // "<init>":()V
  #29 = Utf8               Test1
  #30 = NameAndType        #21:#16        // m1:()V
  #31 = NameAndType        #24:#16        // m2:()V
  #32 = Utf8
  #33 = Utf8               java/lang/StringBuilder
  #34 = NameAndType        #43:#44        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #35 = NameAndType        #43:#45        // append:(I)Ljava/lang/StringBuilder;
  #36 = NameAndType        #46:#47        // toString:()Ljava/lang/String;
  #37 = Class              #48            // java/lang/System
  #38 = NameAndType        #49:#50        // out:Ljava/io/PrintStream;
  #39 = Class              #51            // java/io/PrintStream
  #40 = NameAndType        #52:#53        // println:(Ljava/lang/String;)V
  #41 = Utf8               java/lang/Object
  #42 = Utf8               java/lang/String
  #43 = Utf8               append
  #44 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #45 = Utf8               (I)Ljava/lang/StringBuilder;
  #46 = Utf8               toString
  #47 = Utf8               ()Ljava/lang/String;
  #48 = Utf8               java/lang/System
  #49 = Utf8               out
  #50 = Utf8               Ljava/io/PrintStream;
  #51 = Utf8               java/io/PrintStream
  #52 = Utf8               println
  #53 = Utf8               (Ljava/lang/String;)V
{
  public Test1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: new           #2                  // class Test1
         3: dup
         4: invokespecial #3                  // Method "<init>":()V
         7: invokevirtual #4                  // Method m1:()V
        10: new           #2                  // class Test1
        13: dup
        14: invokespecial #3                  // Method "<init>":()V
        17: invokevirtual #5                  // Method m2:()V
        20: return
      LineNumberTable:
        line 3: 0
        line 4: 10
        line 5: 20

  public void m1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #6                  // String
         2: astore_1
         3: iconst_0
         4: istore_2
         5: iload_2
         6: iconst_5
         7: if_icmpge     35
        10: new           #7                  // class java/lang/StringBuilder
        13: dup
        14: invokespecial #8                  // Method java/lang/StringBuilder."<init>":()V
        17: aload_1
        18: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        21: iload_2
        22: invokevirtual #10                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        25: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        28: astore_1
        29: iinc          2, 1
        32: goto          5
        35: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
        38: aload_1
        39: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        42: return
      LineNumberTable:
        line 7: 0
        line 8: 3
        line 9: 10
        line 8: 29
        line 11: 35
        line 12: 42
      StackMapTable: number_of_entries = 2
        frame_type = 253 /* append */
          offset_delta = 5
          locals = [ class java/lang/String, int ]
        frame_type = 250 /* chop */
          offset_delta = 29

  public void m2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #7                  // class java/lang/StringBuilder
         3: dup
         4: invokespecial #8                  // Method java/lang/StringBuilder."<init>":()V
         7: astore_1
         8: iconst_0
         9: istore_2
        10: iload_2
        11: iconst_5
        12: if_icmpge     27
        15: aload_1
        16: iload_2
        17: invokevirtual #10                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        20: pop
        21: iinc          2, 1
        24: goto          10
        27: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
        30: aload_1
        31: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        34: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        37: return
      LineNumberTable:
        line 14: 0
        line 15: 8
        line 16: 15
        line 15: 21
        line 18: 27
        line 19: 37
      StackMapTable: number_of_entries = 2
        frame_type = 253 /* append */
          offset_delta = 10
          locals = [ class java/lang/StringBuilder, int ]
        frame_type = 250 /* chop */
          offset_delta = 16
}
SourceFile: "Test1.java"

String字节码详解
在这里插入图片描述
可以看出每次循环的时候都new了一个StringBuilder

StringBuilder字节码详解
在这里插入图片描述
可以看出在循环外面new的StringBuilder所以无须每次循环都去开辟一个空间存放StringBuilder对象,节约了空间

3.总结

  1. String和StringBuilder本质没有区别,都是new StringBuilder调用append方法进行字符串相加
  2. 如果在循环中有字符串的操作,最好使用StringBuilder

纯手写点个赞!

猜你喜欢

转载自blog.csdn.net/VinceZxy/article/details/89355048