字符串拼接
还记得初学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
字符串匹配
构造字典树(也称前缀树)来匹配对应的文字。