程式程式碼
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = a + b;
System.out.println(c);
}
}
編譯Test.java
javac -g:vars Test.java
使用-g:vars
會生成本地變數表資訊
將Test.class
轉換成位元組碼輸出到控制檯
javap -v -l Test.class
或者輸出到檔案中
javap -v -l Test.class > test.txt
位元組碼檔案內容如下所示
Classfile /D:/LZC/code/example/javacode/demo/src/main/java/Test.class
Last modified 2020-4-23; size 457 bytes
MD5 checksum 8afe0a5acd5640f0053847a86b66d084
public class Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#20 // java/lang/Object."<init>":()V
#2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #23.#24 // java/io/PrintStream.println:(I)V
#4 = Class #25 // Test
#5 = Class #26 // java/lang/Object
#6 = Utf8 <init>
#7 = Utf8 ()V
#8 = Utf8 Code
#9 = Utf8 LocalVariableTable
#10 = Utf8 this
#11 = Utf8 LTest;
#12 = Utf8 main
#13 = Utf8 ([Ljava/lang/String;)V
#14 = Utf8 args
#15 = Utf8 [Ljava/lang/String;
#16 = Utf8 a
#17 = Utf8 I
#18 = Utf8 b
#19 = Utf8 c
#20 = NameAndType #6:#7 // "<init>":()V
#21 = Class #27 // java/lang/System
#22 = NameAndType #28:#29 // out:Ljava/io/PrintStream;
#23 = Class #30 // java/io/PrintStream
#24 = NameAndType #31:#32 // println:(I)V
#25 = Utf8 Test
#26 = Utf8 java/lang/Object
#27 = Utf8 java/lang/System
#28 = Utf8 out
#29 = Utf8 Ljava/io/PrintStream;
#30 = Utf8 java/io/PrintStream
#31 = Utf8 println
#32 = Utf8 (I)V
{
public Test();
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
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: bipush 10
2: istore_1
3: bipush 20
5: istore_2
6: iload_1
7: iload_2
8: iadd
9: istore_3
10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
13: iload_3
14: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
17: return
LocalVariableTable:
Start Length Slot Name Signature
0 18 0 args [Ljava/lang/String;
3 15 1 a I
6 12 2 b I
10 8 3 c I
}
LocalVariableTable描述了本地變數表,可以看出,程式中共有四個本地變數,Slot
代表變數索引位置,Name
代表變數名字。
分析位元組碼執行過程
// 將10入棧
0: bipush 10
// 出棧,並將值儲存到索引為1的本地變數中
2: istore_1
// 將20入棧
3: bipush 20
// 出棧,並將值儲存到索引為2的本地變數中
5: istore_2
// 將索引為1的變數值入棧
6: iload_1
// 將索引為2的變數值入棧
7: iload_2
// 執行int型別的加法,將10和20出棧並將這兩個值相加,最後把相加結果如棧,即將30入棧
8: iadd
// 出棧,並將值儲存到索引為3的本地變數中
9: istore_3
// 將PrintStream入棧
10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
// 將索引為3的變數值入棧
13: iload_3
// 出棧,出棧。執行PrintStream.println方法
14: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
17: return
i++位元組碼
程式碼
public class Test {
public static void main(String[] args) {
int a = 10;
int b = a++;
System.out.println(b);
}
}
位元組碼
// 將10入棧
0: bipush 10
// 出棧,將10儲存到索引為1的本地變數中,即 a = 10;
2: istore_1
// 將索引為1的本地變數值入棧
3: iload_1
// iinc indexbyte,constbyte
// 將整數值constbyte加到indexbyte指定的int型別的區域性變數中。
// 將本地變數a的值加1,此時a=11,但此時棧裡面的值仍然為10
4: iinc 1, 1
// 出棧(此時棧頂元素為10),將值儲存到索引為2的本地變數中,即 b = 10
7: istore_2
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_2
12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
15: return
// 本地變數表
LocalVariableTable:
Start Length Slot Name Signature
0 16 0 args [Ljava/lang/String;
3 13 1 a I
8 8 2 b I
++i位元組碼
程式碼
public class Test {
public static void main(String[] args) {
int a = 10;
int b = ++a;
System.out.println(b);
}
}
位元組碼
// 將10入棧
0: bipush 10
// 出棧,將10儲存到索引為1的本地變數中,即 a = 10;
2: istore_1
// iinc indexbyte,constbyte
// 將整數值constbyte加到indexbyte指定的int型別的區域性變數中。
// 將本地變數a的值加1,此時a=11
3: iinc 1, 1
// 將索引為1的本地變數值入棧,即將11入棧
6: iload_1
// 出棧(此時棧頂元素為11),將值儲存到索引為2的本地變數中,即 b = 11
7: istore_2
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_2
12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
15: return
LocalVariableTable:
Start Length Slot Name Signature
0 16 0 args [Ljava/lang/String;
3 13 1 a I
8 8 2 b I
區別
i++是先將本地變數值入棧,然後將本地變數值加1;
++i是先將本地變數值加1,然後再將本地變數入棧。
程式碼
/**
* Java環境為JDK8,如果是JDK6,列印結果會不一樣
* JDK6時,常量池放在永久代
* JDK7開始,常量池放在堆上
* JDK8開始,元空間取代了永久代
*/
public class Test {
public static void main(String[] args) {
// 在堆裡面建立一個"11"物件。此時常量池裡面沒有"11",所以在常量池裡面也建立"11"
String a1 = new String("11");
// 此時常量池裡面已經有了"11"了,這裡直接返回常量池裡面的引用
a1.intern();
// 將b1指向常量池裡面的"11"
String b1 = "11";
// a1指向堆裡面的地址,b1指向常量池裡面的地址,所以這裡為false
System.out.println(a1 == b1); // false
// 這裡會在堆裡面建立"aa",在常量池裡面建立"a"
String a2 = new String("a") + new String("a");
// 常量池裡面沒有"aa",所以講這個物件新增到常量池中
// 這裡並不是直接在常量池裡面新建一個"aa"物件
// 而是生成一個指向堆裡面"aa"物件的一個引用
a2.intern();
// 將b2指向常量池裡面的"aa"
String b2 = "aa";
System.out.println(a2 == b2); // true
}
}
位元組碼
Classfile /D:/LZC/code/example/javacode/demo/src/main/java/Test.class
Last modified 2020-4-23; size 936 bytes
MD5 checksum 807cfb9720864177ebe7f056d1031b6b
public class Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #15.#35 // java/lang/Object."<init>":()V
#2 = Class #36 // java/lang/String
#3 = String #37 // 11
#4 = Methodref #2.#38 // java/lang/String."<init>":(Ljava/lang/String;)V
#5 = Methodref #2.#39 // java/lang/String.intern:()Ljava/lang/String;
#6 = Fieldref #40.#41 // java/lang/System.out:Ljava/io/PrintStream;
#7 = Methodref #42.#43 // java/io/PrintStream.println:(Z)V
#8 = Class #44 // java/lang/StringBuilder
#9 = Methodref #8.#35 // java/lang/StringBuilder."<init>":()V
#10 = String #45 // a
#11 = Methodref #8.#46 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#12 = Methodref #8.#47 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#13 = String #48 // aa
#14 = Class #49 // Test
#15 = Class #50 // java/lang/Object
#16 = Utf8 <init>
#17 = Utf8 ()V
#18 = Utf8 Code
#19 = Utf8 LocalVariableTable
#20 = Utf8 this
#21 = Utf8 LTest;
#22 = Utf8 main
#23 = Utf8 ([Ljava/lang/String;)V
#24 = Utf8 args
#25 = Utf8 [Ljava/lang/String;
#26 = Utf8 a1
#27 = Utf8 Ljava/lang/String;
#28 = Utf8 b1
#29 = Utf8 a2
#30 = Utf8 b2
#31 = Utf8 StackMapTable
#32 = Class #25 // "[Ljava/lang/String;"
#33 = Class #36 // java/lang/String
#34 = Class #51 // java/io/PrintStream
#35 = NameAndType #16:#17 // "<init>":()V
#36 = Utf8 java/lang/String
#37 = Utf8 11
#38 = NameAndType #16:#52 // "<init>":(Ljava/lang/String;)V
#39 = NameAndType #53:#54 // intern:()Ljava/lang/String;
#40 = Class #55 // java/lang/System
#41 = NameAndType #56:#57 // out:Ljava/io/PrintStream;
#42 = Class #51 // java/io/PrintStream
#43 = NameAndType #58:#59 // println:(Z)V
#44 = Utf8 java/lang/StringBuilder
#45 = Utf8 a
#46 = NameAndType #60:#61 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#47 = NameAndType #62:#54 // toString:()Ljava/lang/String;
#48 = Utf8 aa
#49 = Utf8 Test
#50 = Utf8 java/lang/Object
#51 = Utf8 java/io/PrintStream
#52 = Utf8 (Ljava/lang/String;)V
#53 = Utf8 intern
#54 = Utf8 ()Ljava/lang/String;
#55 = Utf8 java/lang/System
#56 = Utf8 out
#57 = Utf8 Ljava/io/PrintStream;
#58 = Utf8 println
#59 = Utf8 (Z)V
#60 = Utf8 append
#61 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#62 = Utf8 toString
{
public Test();
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
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=5, args_size=1
0: new #2 // class java/lang/String
3: dup
4: ldc #3 // String 11
6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
10: aload_1
11: invokevirtual #5 // Method java/lang/String.intern:()Ljava/lang/String;
14: pop
15: ldc #3 // String 11
17: astore_2
18: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
21: aload_1
22: aload_2
23: if_acmpne 30
26: iconst_1
27: goto 31
30: iconst_0
31: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V
34: new #8 // class java/lang/StringBuilder
37: dup
38: invokespecial #9 // Method java/lang/StringBuilder."<init>":()V
41: new #2 // class java/lang/String
44: dup
45: ldc #10 // String a
47: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
50: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
53: new #2 // class java/lang/String
56: dup
57: ldc #10 // String a
59: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
62: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
65: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
68: astore_3
69: aload_3
70: invokevirtual #5 // Method java/lang/String.intern:()Ljava/lang/String;
73: pop
74: ldc #13 // String aa
76: astore 4
78: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
81: aload_3
82: aload 4
84: if_acmpne 91
87: iconst_1
88: goto 92
91: iconst_0
92: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V
95: return
LocalVariableTable:
Start Length Slot Name Signature
0 96 0 args [Ljava/lang/String;
10 86 1 a1 Ljava/lang/String;
18 78 2 b1 Ljava/lang/String;
69 27 3 a2 Ljava/lang/String;
78 18 4 b2 Ljava/lang/String;
StackMapTable: number_of_entries = 4
frame_type = 255 /* full_frame */
offset_delta = 30
locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream, int ]
frame_type = 255 /* full_frame */
offset_delta = 59
locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream ]
frame_type = 255 /* full_frame */
offset_delta = 0
locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String, class java/lang/String ]
stack = [ class java/io/PrintStream, int ]
}
檢視位元組碼可以看出,字串變數使用加號連線,底層是使用StringBuilder.append
實現的。
以下情況的字串相加會使用StringBuilder.append
實現
String a2 = new String("a") + new String("a");
以下情況的字串相加在程式編譯的時候就會將字串拼接好了
String a = "11" + "22";
final String b = "33";
final String c = "44";
String d = b + c;
本作品採用《CC 協議》,轉載必須註明作者和本文連結