探索字节码底层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.总结
- String和StringBuilder本质没有区别,都是new StringBuilder调用append方法进行字符串相加
- 如果在循环中有字符串的操作,最好使用StringBuilder
纯手写点个赞!