这里都是针对jdk1.8的hotspot虚拟机讲解
public class Demo1_1{
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1+s2;
String s5 = "a" + "b";
}
}
通过在命令窗口反编译class文件得到如下信息: 认识二进制字节码文件内容(三)
D:\workspace\day01easyjdbc01\target\classes\com\demo\jvm>javap -v Demo1_1.class
Classfile /D:/workspace/day01easyjdbc01/target/classes/com/demo/jvm/Demo1_1.class
Last modified 2020-3-29; size 706 bytes
MD5 checksum 0ba87b6925bb2d842174a3e3f4a1c3df
Compiled from "Demo1_1.java"
public class com.demo.jvm.Demo1_1
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #10.#30 // java/lang/Object."":()V
#2 = String #31 // a
#3 = String #32 // b
#4 = String #33 // ab
#5 = Class #34 // java/lang/StringBuilder
#6 = Methodref #5.#30 // java/lang/StringBuilder."":()V
#7 = Methodref #5.#35 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#8 = Methodref #5.#36 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#9 = Class #37 // com/demo/jvm/Demo1_1
#10 = Class #38 // java/lang/Object
#11 = Utf8
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 LocalVariableTable
#16 = Utf8 this
#17 = Utf8 Lcom/demo/jvm/Demo1_1;
#18 = Utf8 main
#19 = Utf8 ([Ljava/lang/String;)V
#20 = Utf8 args
#21 = Utf8 [Ljava/lang/String;
#22 = Utf8 s1
#23 = Utf8 Ljava/lang/String;
#24 = Utf8 s2
#25 = Utf8 s3
#26 = Utf8 s4
#27 = Utf8 s5
#28 = Utf8 SourceFile
#29 = Utf8 Demo1_1.java
#30 = NameAndType #11:#12 // "":()V
#31 = Utf8 a
#32 = Utf8 b
#33 = Utf8 ab
#34 = Utf8 java/lang/StringBuilder
#35 = NameAndType #39:#40 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#36 = NameAndType #41:#42 // toString:()Ljava/lang/String;
#37 = Utf8 com/demo/jvm/Demo1_1
#38 = Utf8 java/lang/Object
#39 = Utf8 append
#40 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#41 = Utf8 toString
#42 = Utf8 ()Ljava/lang/String;
{
public com.demo.jvm.Demo1_1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/demo/jvm/Demo1_1;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=6, args_size=1
0: ldc #2 // String a
2: astore_1
3: ldc #3 // String b
5: astore_2
6: ldc #4 // String ab
8: astore_3
9: new #5 // class java/lang/StringBuilder
12: dup
13: invokespecial #6 // Method java/lang/StringBuilder."":()V
16: aload_1
17: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: aload_2
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 4
29: ldc #4 // String ab
31: astore 5
33: return
LineNumberTable:
line 5: 0
line 6: 3
line 7: 6
line 8: 9
line 9: 29
line 10: 33
LocalVariableTable:
Start Length Slot Name Signature
0 34 0 args [Ljava/lang/String;
3 31 1 s1 Ljava/lang/String;
6 28 2 s2 Ljava/lang/String;
9 25 3 s3 Ljava/lang/String;
29 5 4 s4 Ljava/lang/String;
33 1 5 s5 Ljava/lang/String;
}
SourceFile: "Demo1_1.java"
1. String s1 = "a" 代码说明
根据虚拟机指令0,解释器执行了运行时常量池里面的#2,这个时候常量池从空值状态变成了存放了一个符号a(这里只是一个符号),然后根据#31 加载字符串对象"a",常量"a" 压入操作栈
接着执行虚拟机指令2,执行astore_1把刚才的字符串对象"a"保存到LocalVariableTable局部变量表中Slot位置1,变量名是s1
最后就是jvm栈中变量名为s1的指向堆内存中的常量池里面的"a"
同理 String s2 = "b"
最后把字符串对象"b"保存到局部变量表的中位置2,变量名为s2.
2.接下来在看看String s3 = "ab" 代码说明:
这里执行虚拟机指令9,调用new关键字 最后在堆空间创建了一个StringBuilder对象。
执行指令13,调用了init的无参构造函数
执行指令16,调用aload_1,从局部变量表中拿到s1,
执行指令17,调用StringBuilder.append方法,把s1传入方法中,
同理执行20.21,把s2传入方法中
执行24,调用StrinbBuilder.toString方法
执行27,执行 astore 4 ,把toString 转换后的结果存入局部变量表中的4号位置。
通过查看StringBuilder源代码中toString方法,也可以看出是new了一个新的字符串对象。
3.接着看String s5 = "a" + "b"说明
执指令行29,找到的常量池中#4,也就是符号ab,也就是说常量池中如果已经存在要找的字符串,不需要在存放,直接拿来使用即可
执行指令31,存放到局部变量表中位置5
执行33,方法结束
小知识点:可以通过调用intern()方法,尝试讲字符串放入常量池中,有则放入,并返回串池中的对象
1.6版本中,如果串池中没有当前放入的常量,则是copy一份放入串池的
String a1 = "a" + "b" ;
String a2 = a.intern();
总结: