字符串拼接
還記得初學Java時,字符串拼接要用StringBuilder#append
方法,最後用toString
方法得出最後的字符串結果。
但其實JDK會自動將使用“+”拼接轉換爲StringBuilder,虛擬機使用-XX:+OptimizeStringConcat
開啓字符串拼接優化功能。
a+b 會被虛擬機編譯成 new StringBuilder().append(a).append(b).toString();
驗證過程
首先敲一段代碼
public class StringTest {
public void add() {
String a = "hello";
String b = "world";
String c = a + b;
System.out.println(c);
}
}
右鍵文件進入終端,執行javac命令獲取class文件
得出以下結果,後面的英文註釋都是自動加上的,極大方便閱讀,也可見StringBuilder的創建時機。
# mac @ macdeMacBook-Pro in ~/IdeaProjects/exam/src/com/xxx/test/base [6:46:12]
$ javac StringTest.java
# mac @ macdeMacBook-Pro in ~/IdeaProjects/exam/src/com/xxx/test/base [6:46:21]
$ ls
StringTest.class StringTest.java
# mac @ macdeMacBook-Pro in ~/IdeaProjects/exam/src/com/xxx/test/base [6:46:23]
$ javap -v StringTest.class
Classfile /Users/mac/IdeaProjects/exam/src/com/xxx/test/base/StringTest.class
Last modified 2022-1-24; size 606 bytes
MD5 checksum d585fe78e850590fdf95a13b80806a03
Compiled from "StringTest.java"
public class com.huawei.test.base.StringTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #11.#19 // java/lang/Object."<init>":()V
#2 = String #20 // hello
#3 = String #21 // world
#4 = Class #22 // java/lang/StringBuilder
#5 = Methodref #4.#19 // java/lang/StringBuilder."<init>":()V
#6 = Methodref #4.#23 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#7 = Methodref #4.#24 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#8 = Fieldref #25.#26 // java/lang/System.out:Ljava/io/PrintStream;
#9 = Methodref #27.#28 // java/io/PrintStream.println:(Ljava/lang/String;)V
#10 = Class #29 // com/huawei/test/base/StringTest
#11 = Class #30 // java/lang/Object
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 add
#17 = Utf8 SourceFile
#18 = Utf8 StringTest.java
#19 = NameAndType #12:#13 // "<init>":()V
#20 = Utf8 hello
#21 = Utf8 world
#22 = Utf8 java/lang/StringBuilder
#23 = NameAndType #31:#32 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#24 = NameAndType #33:#34 // toString:()Ljava/lang/String;
#25 = Class #35 // java/lang/System
#26 = NameAndType #36:#37 // out:Ljava/io/PrintStream;
#27 = Class #38 // java/io/PrintStream
#28 = NameAndType #39:#40 // println:(Ljava/lang/String;)V
#29 = Utf8 com/huawei/test/base/StringTest
#30 = Utf8 java/lang/Object
#31 = Utf8 append
#32 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#33 = Utf8 toString
#34 = Utf8 ()Ljava/lang/String;
#35 = Utf8 java/lang/System
#36 = Utf8 out
#37 = Utf8 Ljava/io/PrintStream;
#38 = Utf8 java/io/PrintStream
#39 = Utf8 println
#40 = Utf8 (Ljava/lang/String;)V
{
public com.huawei.test.base.StringTest();
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 8: 0
public void add();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: ldc #2 // String hello
2: astore_1
3: ldc #3 // String world
5: astore_2
6: new #4 // class java/lang/StringBuilder
9: dup
10: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
13: aload_1
14: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_2
18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_3
25: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
28: aload_3
29: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
32: return
LineNumberTable:
line 10: 0
line 11: 3
line 12: 6
line 13: 25
line 14: 32
}
SourceFile: "StringTest.java"
令人驚奇的是以下代碼在JIT不會被優化。
StringBuilder sb = new StringBuilder();
sb.apend(a);
sb.append(b)
當然還有StringBuffer
這種線程安全的類及方法,在大部分場景下的字符串拼接是不涉及線程同步的。so 比較少用。
字符串格式化
在項目中記錄日誌時通常會用 log.info("物流單已生成,單號:{}",orderNumber)
,此類的佔位符,並且後續的參數是不定長的,可以擴展多個佔位符,非常好用。
String.format
String fmt = "hello %s";
String res = String.format(fmt,"world"); // hello world
MessageFormat.format
其實在JDK包也提供了MessageFormat#format
方法來實現類似的功能。部分源碼如下:
package java.text; // 屬於java text包下
public class MessageFormat extends Format {
// ...
//核心方法
public static String format(String var0, Object... var1) {
MessageFormat var2 = new MessageFormat(var0);
return var2.format(var1); //此處調用父類Format的format方法。format(obj, new StringBuffer(), new FieldPosition(0)).toString();
}
}
使用方法
MessageFormat.format("myname is {0},age is {1}","Lucy","12"); // myname is Lucy,age is 12
字符串匹配
構造字典樹(也稱前綴樹)來匹配對應的文字。