1.StringBuilder
??我們在對做字符串拼接時每瞒,如果采用如下操作:
String result = "";
for(String str : strArr){
result += str;
}
??確實能夠達到拼接字符串的目的艺普,但是會在字符串常量池中創(chuàng)建多個字符串對象华畏,會浪費大量的內存谨敛,并且影響GC(垃圾收集)效率媳荒。為了解決這個問題抗悍,Java標準庫提供了StringBuilder類以實現(xiàn)字符串拼接。StringBuilder與String不同钳枕,它的對象是可變對象缴渊,并且會預分配緩沖區(qū),如此就不會創(chuàng)建多個對象鱼炒,只用創(chuàng)建一個StringBuilder對象即可衔沼。
??一般情況下,對于字符串間的+
操作,并不需要用StringBuilder指蚁。因為Java編譯時會把字符串+
操作編譯為StringConcatFactory的操作菩佑,自動將字符串拼接優(yōu)化為數(shù)組復制或StringBuilder的操作。
2.從下面兩道面試題展開討論:
1
String str = "a" + "b";
此條語句創(chuàng)建了多少個對象凝化?
2String str = new String("a" + "b") + "a" + "b";
此條語句創(chuàng)建了多少個對象稍坯?
??第一條語句,Java編譯時會將"a"+"b"
直接編譯為"ab"
搓劫,如此原語句等同于String str = "ab";
瞧哟,這樣會在字符串常量池中創(chuàng)建一個字符串對象。
??對于本例枪向,我們編寫代碼并使用javap進行解析:
public class cp1 {
public static void main(String[] args){
String s = "a" + "b";
System.out.println(s);
}
}
??使用javac -g cp1.java
編譯源碼之后勤揩,使用javap -v cp1
查看解析結果:
Classfile /E:/Java學習/src/cp1.class
Last modified 2021-10-19; size 544 bytes
MD5 checksum e771532b8aacc4a59518aae9ac7faddd
Compiled from "cp1.java"
public class cp1
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#22 // java/lang/Object."<init>":()V
#2 = String #23 // ab
#3 = Fieldref #24.#25 // java/lang/System.out:Ljava/io/PrintStream;
#4 = Methodref #26.#27 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #28 // cp1
#6 = Class #29 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcp1;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 s
#19 = Utf8 Ljava/lang/String;
#20 = Utf8 SourceFile
#21 = Utf8 cp1.java
#22 = NameAndType #7:#8 // "<init>":()V
#23 = Utf8 ab
#24 = Class #30 // java/lang/System
#25 = NameAndType #31:#32 // out:Ljava/io/PrintStream;
#26 = Class #33 // java/io/PrintStream
#27 = NameAndType #34:#35 // println:(Ljava/lang/String;)V
#28 = Utf8 cp1
#29 = Utf8 java/lang/Object
#30 = Utf8 java/lang/System
#31 = Utf8 out
#32 = Utf8 Ljava/io/PrintStream;
#33 = Utf8 java/io/PrintStream
#34 = Utf8 println
#35 = Utf8 (Ljava/lang/String;)V
{
public cp1();
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 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcp1;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: ldc #2 // String ab
2: astore_1
3: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_1
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
LineNumberTable:
line 13: 0
line 14: 3
line 15: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 args [Ljava/lang/String;
3 8 1 s Ljava/lang/String;
}
SourceFile: "cp1.java"
??可以看到在Constant pool中新創(chuàng)建了一個字符串對象"ab"
,在堆中并沒有創(chuàng)建遣疯⌒劭桑總計只創(chuàng)建了一個字符串對象。
??第二條語句缠犀,new String("a" + "b") + "a" + "b";
中数苫,同樣的編譯器將"a"+"b"
直接編譯為"ab"
,會在字符串常量池中創(chuàng)建"ab"
一個字符串對象辨液,然后在堆中創(chuàng)建一個"ab"
字符串對象虐急。然后調用StringBuilder的append()方法拼接字符串后,調用toString()方法返回了一個字符串對象"abab"
滔迈。
??對第二例采用相同的方法進行解析:
// 源碼
public class cp1 {
String s = new String("a" + "b") + "a" + "b";
System.out.println(s);
}
}
// javap 解析結果
Classfile /E:/Java學習/src/cp1.class
Last modified 2021-10-19; size 746 bytes
MD5 checksum 4d1188a58dd3d189626ca06c1a5f9e18
Compiled from "cp1.java"
public class cp1
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #12.#28 // java/lang/Object."<init>":()V
#2 = Class #29 // java/lang/StringBuilder
#3 = Methodref #2.#28 // java/lang/StringBuilder."<init>":()V
#4 = Class #30 // java/lang/String
#5 = String #31 // ab
#6 = Methodref #4.#32 // java/lang/String."<init>":(Ljava/lang/String;)V
#7 = Methodref #2.#33 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#8 = Methodref #2.#34 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#9 = Fieldref #35.#36 // java/lang/System.out:Ljava/io/PrintStream;
#10 = Methodref #37.#38 // java/io/PrintStream.println:(Ljava/lang/String;)V
#11 = Class #39 // cp1
#12 = Class #40 // java/lang/Object
#13 = Utf8 <init>
#14 = Utf8 ()V
#15 = Utf8 Code
#16 = Utf8 LineNumberTable
#17 = Utf8 LocalVariableTable
#18 = Utf8 this
#19 = Utf8 Lcp1;
#20 = Utf8 main
#21 = Utf8 ([Ljava/lang/String;)V
#22 = Utf8 args
#23 = Utf8 [Ljava/lang/String;
#24 = Utf8 s
#25 = Utf8 Ljava/lang/String;
#26 = Utf8 SourceFile
#27 = Utf8 cp1.java
#28 = NameAndType #13:#14 // "<init>":()V
#29 = Utf8 java/lang/StringBuilder
#30 = Utf8 java/lang/String
#31 = Utf8 ab
#32 = NameAndType #13:#41 // "<init>":(Ljava/lang/String;)V
#33 = NameAndType #42:#43 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#34 = NameAndType #44:#45 // toString:()Ljava/lang/String;
#35 = Class #46 // java/lang/System
#36 = NameAndType #47:#48 // out:Ljava/io/PrintStream;
#37 = Class #49 // java/io/PrintStream
#38 = NameAndType #50:#41 // println:(Ljava/lang/String;)V
#39 = Utf8 cp1
#40 = Utf8 java/lang/Object
#41 = Utf8 (Ljava/lang/String;)V
#42 = Utf8 append
#43 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#44 = Utf8 toString
#45 = Utf8 ()Ljava/lang/String;
#46 = Utf8 java/lang/System
#47 = Utf8 out
#48 = Utf8 Ljava/io/PrintStream;
#49 = Utf8 java/io/PrintStream
#50 = Utf8 println
{
public cp1();
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 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcp1;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=2, args_size=1
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: new #4 // class java/lang/String
10: dup
11: ldc #5 // String ab
13: invokespecial #6 // Method java/lang/String."<init>":(Ljava/lang/String;)V
16: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #5 // String ab
21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: astore_1
28: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
31: aload_1
32: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
35: return
LineNumberTable:
line 13: 0
line 14: 28
line 15: 35
LocalVariableTable:
Start Length Slot Name Signature
0 36 0 args [Ljava/lang/String;
28 8 1 s Ljava/lang/String;
}
SourceFile: "cp1.java"
??可以看到在常量池中創(chuàng)建了一個字符串對象"ab"
止吁,在堆中創(chuàng)建了一個StringBuilder對象,一個字符串對象"ab"
燎悍,并且調用StringBuilder對象的toString()方法返回了一個字符串對象"abab"
敬惦。總計創(chuàng)建了三個字符串對象谈山,一個StringBuilder對象俄删。