为了验证编译器对于String相关代码的字节码优化,我简单的做了个测试:
jdk版本:1.7.0_51
java 用例代码:
package com.rsp.shiro;
/**
* Created by cd_huang on 2017/4/1.
*/
public class StringTest {
public static String StaticString(){
return "BBB";
}
public static String getString1(){
return "AAA"+"BBB";
}
public static String getString2(){
return new StringBuilder().append("AAA").append("BBB").toString();
}
public static String getString3(){
return "AAA"+StaticString();
}
public static String getString4(){
return new StringBuilder().append("AAA").append(StaticString()).toString();
}
}
命令行输入:C:\Users\hcd>javac c:\StringTest.java
生成StringTest.class文件。
命令行输入:C:\Users\hcd>javap -v c:\StringTest.class
生成java编译后的字节码指令。
Classfile /c:/StringTest.class
Last modified 2017-4-1; size 721 bytes
MD5 checksum 60b413955d7484157009f74a40848d59
Compiled from "StringTest.java"
public class com.rsp.shiro.StringTest
SourceFile: "StringTest.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #11.#24 // java/lang/Object."<init>":()V
#2 = String #25 // BBB
#3 = String #26 // AAABBB
#4 = Class #27 // java/lang/StringBuilder
#5 = Methodref #4.#24 // java/lang/StringBuilder."<init>":(
)V
#6 = String #28 // AAA
#7 = Methodref #4.#29 // java/lang/StringBuilder.append:(Lj
ava/lang/String;)Ljava/lang/StringBuilder;
#8 = Methodref #4.#30 // java/lang/StringBuilder.toString:(
)Ljava/lang/String;
#9 = Methodref #10.#31 // com/rsp/shiro/StringTest.StaticStr
ing:()Ljava/lang/String;
#10 = Class #32 // com/rsp/shiro/StringTest
#11 = Class #33 // java/lang/Object
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 StaticString
#17 = Utf8 ()Ljava/lang/String;
#18 = Utf8 getString1
#19 = Utf8 getString2
#20 = Utf8 getString3
#21 = Utf8 getString4
#22 = Utf8 SourceFile
#23 = Utf8 StringTest.java
#24 = NameAndType #12:#13 // "<init>":()V
#25 = Utf8 BBB
#26 = Utf8 AAABBB
#27 = Utf8 java/lang/StringBuilder
#28 = Utf8 AAA
#29 = NameAndType #34:#35 // append:(Ljava/lang/String;)Ljava/l
ang/StringBuilder;
#30 = NameAndType #36:#17 // toString:()Ljava/lang/String;
#31 = NameAndType #16:#17 // StaticString:()Ljava/lang/String;
#32 = Utf8 com/rsp/shiro/StringTest
#33 = Utf8 java/lang/Object
#34 = Utf8 append
#35 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#36 = Utf8 toString
{
public com.rsp.shiro.StringTest();
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 6: 0
public static java.lang.String StaticString();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: ldc #2 // String BBB
2: areturn
LineNumberTable:
line 8: 0
public static java.lang.String getString1();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: ldc #3 // String AAABBB
2: areturn
LineNumberTable:
line 11: 0
public static java.lang.String getString2();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: new #4 // class java/lang/StringBuilder
3: dup
4: invokespecial #5 // Method java/lang/StringBuilder.
"<init>":()V
7: ldc #6 // String AAA
9: invokevirtual #7 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
12: ldc #2 // String BBB
14: invokevirtual #7 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: invokevirtual #8 // Method java/lang/StringBuilder.
toString:()Ljava/lang/String;
20: areturn
LineNumberTable:
line 14: 0
public static java.lang.String getString3();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: new #4 // class java/lang/StringBuilder
3: dup
4: invokespecial #5 // Method java/lang/StringBuilder.
"<init>":()V
7: ldc #6 // String AAA
9: invokevirtual #7 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
12: invokestatic #9 // Method StaticString:()Ljava/lan
g/String;
15: invokevirtual #7 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18: invokevirtual #8 // Method java/lang/StringBuilder.
toString:()Ljava/lang/String;
21: areturn
LineNumberTable:
line 17: 0
public static java.lang.String getString4();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: new #4 // class java/lang/StringBuilder
3: dup
4: invokespecial #5 // Method java/lang/StringBuilder.
"<init>":()V
7: ldc #6 // String AAA
9: invokevirtual #7 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
12: invokestatic #9 // Method StaticString:()Ljava/lan
g/String;
15: invokevirtual #7 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18: invokevirtual #8 // Method java/lang/StringBuilder.
toString:()Ljava/lang/String;
21: areturn
LineNumberTable:
line 20: 0
}
C:\Users\hcd>
可以看出来getString3() 方法和getString4()方法的字节码指令一摸一样,也就是编译器帮我们做了优化。
由于String对象时不可变对象,因此在对字符串进行拼接时,String对象总是会生成新的对象,所以其性能相对较差。
StringBuilder:一个非线程安全的字符串缓冲类,jdk1.5后出现。
所以,编译器在字符串对象拼接时,自动帮我们用StringBuilder实现了字符串拼接。