package cn.knightzz.jvm.bytecode;
/**
* @author 王天赐
* @title: StaticCode
* @projectName hm-jvm-codes
* @description: 静态代码块
* @website http://knightzz.cn/
* @github https://github.com/knightzz1998
* @create: 2022-10-19 19:42
*/
public class StaticCode {
static {
i = 30;
}
static int i = 10;
static {
i = 20;
}
}
$ javap -v StaticCode.class
Classfile /F:/JavaCode/hm-jvm-codes/jvm-chapter-03/target/classes/cn/knightzz/jvm/bytecode/StaticCode.class
Last modified 2022-10-19; size 408 bytes
MD5 checksum d8dc4cae931bd2602bdb99bd72e511ce
Compiled from "StaticCode.java"
public class cn.knightzz.jvm.bytecode.StaticCode
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#17 // java/lang/Object."":()V
#2 = Fieldref #3.#18 // cn/knightzz/jvm/bytecode/StaticCode.i:I
#3 = Class #19 // cn/knightzz/jvm/bytecode/StaticCode
#4 = Class #20 // java/lang/Object
#5 = Utf8 i
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcn/knightzz/jvm/bytecode/StaticCode;
#14 = Utf8 <clinit>
#15 = Utf8 SourceFile
#16 = Utf8 StaticCode.java
#17 = NameAndType #7:#8 // "":()V
#18 = NameAndType #5:#6 // i:I
#19 = Utf8 cn/knightzz/jvm/bytecode/StaticCode
#20 = Utf8 java/lang/Object
{
static int i;
descriptor: I
flags: ACC_STATIC
public cn.knightzz.jvm.bytecode.StaticCode();
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 12: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/knightzz/jvm/bytecode/StaticCode;
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 10
2: putstatic #2 // Field i:I
5: bipush 20
7: putstatic #2 // Field i:I
10: bipush 30
12: putstatic #2 // Field i:I
15: return
LineNumberTable:
line 14: 0
line 17: 5
line 21: 10
line 22: 15
}
SourceFile: "StaticCode.java"
编译器会按从上至下的顺序,收集所有 static 静态代码块和静态成员赋值的代码,合并为一个特殊的方法 :
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 10
2: putstatic #2 // Field i:I
5: bipush 20
7: putstatic #2 // Field i:I
10: bipush 30
12: putstatic #2 // Field i:I
15: return
方法会在类加载的初始化阶段被调用
package cn.knightzz.jvm.bytecode;
/**
* @author 王天赐
* @title: StaticCode
* @projectName hm-jvm-codes
* @description: 静态代码块
* @website http://knightzz.cn/
* @github https://github.com/knightzz1998
* @create: 2022-10-19 19:42
*/
public class StaticCode {
static {
i = 30;
}
static int i = 10;
static {
i = 20;
}
}
原本的字节码
0: bipush 10
2: putstatic #2 // Field i:I
5: bipush 20
7: putstatic #2 // Field i:I
10: bipush 30
12: putstatic #2 // Field i:I
15: return
改变位置后的字节码 :
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 30
2: putstatic #2 // Field i:I
5: bipush 10
7: putstatic #2 // Field i:I
10: bipush 20
12: putstatic #2 // Field i:I
15: return
初始化方法 :
package cn.knightzz.jvm.bytecode;
/**
* @author 王天赐
* @title: InitCode
* @projectName hm-jvm-codes
* @description:
* @website http://knightzz.cn/
* @github https://github.com/knightzz1998
* @create: 2022-10-19 20:01
*/
public class InitCode {
private String a = "s1";
{
b = 20;
}
private int b = 10;
{
a = "s2";
}
public InitCode(String a, int b) {
this.a = a;
this.b = b;
}
public static void main(String[] args) {
InitCode d = new InitCode("s3", 30);
System.out.println(d.a);
System.out.println(d.b);
}
}
$ javap -v InitCode.class
Classfile /F:/JavaCode/hm-jvm-codes/jvm-chapter-03/target/classes/cn/knightzz/jvm/bytecode/InitCode.class
Last modified 2022-10-19; size 833 bytes
MD5 checksum 91c75c2ea803f91f8b1244a3921a4fda
Compiled from "InitCode.java"
public class cn.knightzz.jvm.bytecode.InitCode
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #12.#31 // java/lang/Object."":()V
#2 = String #32 // s1
#3 = Fieldref #6.#33 // cn/knightzz/jvm/bytecode/InitCode.a:Ljava/lang/String;
#4 = Fieldref #6.#34 // cn/knightzz/jvm/bytecode/InitCode.b:I
#5 = String #35 // s2
#6 = Class #36 // cn/knightzz/jvm/bytecode/InitCode
#7 = String #37 // s3
#8 = Methodref #6.#38 // cn/knightzz/jvm/bytecode/InitCode."":(Ljava/lang/String;I)V
#9 = Fieldref #39.#40 // java/lang/System.out:Ljava/io/PrintStream;
#10 = Methodref #41.#42 // java/io/PrintStream.println:(Ljava/lang/String;)V
#11 = Methodref #41.#43 // java/io/PrintStream.println:(I)V
#12 = Class #44 // java/lang/Object
#13 = Utf8 a
#14 = Utf8 Ljava/lang/String;
#15 = Utf8 b
#16 = Utf8 I
#17 = Utf8 <init>
#18 = Utf8 (Ljava/lang/String;I)V
#19 = Utf8 Code
#20 = Utf8 LineNumberTable
#21 = Utf8 LocalVariableTable
#22 = Utf8 this
#23 = Utf8 Lcn/knightzz/jvm/bytecode/InitCode;
#24 = Utf8 main
#25 = Utf8 ([Ljava/lang/String;)V
#26 = Utf8 args
#27 = Utf8 [Ljava/lang/String;
#28 = Utf8 d
#29 = Utf8 SourceFile
#30 = Utf8 InitCode.java
#31 = NameAndType #17:#45 // "":()V
#32 = Utf8 s1
#33 = NameAndType #13:#14 // a:Ljava/lang/String;
#34 = NameAndType #15:#16 // b:I
#35 = Utf8 s2
#36 = Utf8 cn/knightzz/jvm/bytecode/InitCode
#37 = Utf8 s3
#38 = NameAndType #17:#18 // "":(Ljava/lang/String;I)V
#39 = Class #46 // java/lang/System
#40 = NameAndType #47:#48 // out:Ljava/io/PrintStream;
#41 = Class #49 // java/io/PrintStream
#42 = NameAndType #50:#51 // println:(Ljava/lang/String;)V
#43 = NameAndType #50:#52 // println:(I)V
#44 = Utf8 java/lang/Object
#45 = Utf8 ()V
#46 = Utf8 java/lang/System
#47 = Utf8 out
#48 = Utf8 Ljava/io/PrintStream;
#49 = Utf8 java/io/PrintStream
#50 = Utf8 println
#51 = Utf8 (Ljava/lang/String;)V
#52 = Utf8 (I)V
{
public cn.knightzz.jvm.bytecode.InitCode(java.lang.String, int);
descriptor: (Ljava/lang/String;I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: aload_0
5: ldc #2 // String s1
7: putfield #3 // Field a:Ljava/lang/String;
10: aload_0
11: bipush 20
13: putfield #4 // Field b:I
16: aload_0
17: bipush 10
19: putfield #4 // Field b:I
22: aload_0
23: ldc #5 // String s2
25: putfield #3 // Field a:Ljava/lang/String;
28: aload_0
29: aload_1
30: putfield #3 // Field a:Ljava/lang/String;
33: aload_0
34: iload_2
35: putfield #4 // Field b:I
38: return
LineNumberTable:
line 26: 0
line 14: 4
line 17: 10
line 20: 16
line 23: 22
line 27: 28
line 28: 33
line 29: 38
LocalVariableTable:
Start Length Slot Name Signature
0 39 0 this Lcn/knightzz/jvm/bytecode/InitCode;
0 39 1 a Ljava/lang/String;
0 39 2 b I
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=2, args_size=1
0: new #6 // class cn/knightzz/jvm/bytecode/InitCode
3: dup
4: ldc #7 // String s3
6: bipush 30
8: invokespecial #8 // Method "":(Ljava/lang/String;I)V
11: astore_1
12: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
15: aload_1
16: getfield #3 // Field a:Ljava/lang/String;
19: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
22: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
25: aload_1
26: getfield #4 // Field b:I
29: invokevirtual #11 // Method java/io/PrintStream.println:(I)V
32: return
LineNumberTable:
line 32: 0
line 33: 12
line 34: 22
line 35: 32
LocalVariableTable:
Start Length Slot Name Signature
0 33 0 args [Ljava/lang/String;
12 21 1 d Lcn/knightzz/jvm/bytecode/InitCode;
}
SourceFile: "InitCode.java"
编译器会按从上至下的顺序,收集所有 {} 代码块和成员变量赋值的代码,形成新的构造方法,但原始构造方法内的代码总是在最后
public cn.knightzz.jvm.bytecode.InitCode(java.lang.String, int);
descriptor: (Ljava/lang/String;I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: aload_0 // this
// ---------- private String a = "s1"; ---------------------
5: ldc #2 // String "s1" 将 s1 变量加载到操作数栈
7: putfield #3 // Field a:Ljava/lang/String; ==> this.a = s1
10: aload_0 // this
11: bipush 20
13: putfield #4 // Field b:I
16: aload_0
// ----------- private int b = 10; --------------------------
17: bipush 10
// ---------- a = "s1"; ---------------------
19: putfield #4 // Field b:I
22: aload_0
// ---------- a = "s1"; ---------------------
23: ldc #5 // String s2
//
25: putfield #3 // Field a:Ljava/lang/String; ==> this.a
28: aload_0
29: aload_1
30: putfield #3 // Field a:Ljava/lang/String;
33: aload_0
34: iload_2
35: putfield #4 // Field b:I ==> this.b
38: return
看一下几种不同的方法调用对应的字节码指令
package cn.knightzz.jvm.bytecode;
/**
* @author 王天赐
* @title: MethodInvocation
* @projectName hm-jvm-codes
* @description: 方法调用
* @website http://knightzz.cn/
* @github https://github.com/knightzz1998
* @create: 2022-10-19 20:32
*/
public class MethodInvocation {
public MethodInvocation() {
}
private void test1() {
}
private final void test2() {
}
public void test3() {
}
public static void test4() {
}
public static void main(String[] args) {
MethodInvocation d = new MethodInvocation();
d.test1();
d.test2();
d.test3();
d.test4();
MethodInvocation.test4();
}
}
$ javap -v MethodInvocation.class
Classfile /F:/JavaCode/hm-jvm-codes/jvm-chapter-03/target/classes/cn/knightzz/jvm/bytecode/MethodInvocation.class
Last modified 2022-10-19; size 798 bytes
MD5 checksum e3ab39988b1374e15c9c4019127d11d0
Compiled from "MethodInvocation.java"
public class cn.knightzz.jvm.bytecode.MethodInvocation
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #8.#27 // java/lang/Object."":()V
#2 = Class #28 // cn/knightzz/jvm/bytecode/MethodInvocation
#3 = Methodref #2.#27 // cn/knightzz/jvm/bytecode/MethodInvocation."":()V
#4 = Methodref #2.#29 // cn/knightzz/jvm/bytecode/MethodInvocation.test1:()V
#5 = Methodref #2.#30 // cn/knightzz/jvm/bytecode/MethodInvocation.test2:()V
#6 = Methodref #2.#31 // cn/knightzz/jvm/bytecode/MethodInvocation.test3:()V
#7 = Methodref #2.#32 // cn/knightzz/jvm/bytecode/MethodInvocation.test4:()V
#8 = Class #33 // java/lang/Object
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 LocalVariableTable
#14 = Utf8 this
#15 = Utf8 Lcn/knightzz/jvm/bytecode/MethodInvocation;
#16 = Utf8 test1
#17 = Utf8 test2
#18 = Utf8 test3
#19 = Utf8 test4
#20 = Utf8 main
#21 = Utf8 ([Ljava/lang/String;)V
#22 = Utf8 args
#23 = Utf8 [Ljava/lang/String;
#24 = Utf8 d
#25 = Utf8 SourceFile
#26 = Utf8 MethodInvocation.java
#27 = NameAndType #9:#10 // "":()V
#28 = Utf8 cn/knightzz/jvm/bytecode/MethodInvocation
#29 = NameAndType #16:#10 // test1:()V
#30 = NameAndType #17:#10 // test2:()V
#31 = NameAndType #18:#10 // test3:()V
#32 = NameAndType #19:#10 // test4:()V
#33 = Utf8 java/lang/Object
{
public cn.knightzz.jvm.bytecode.MethodInvocation();
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 14: 0
line 15: 4
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/knightzz/jvm/bytecode/MethodInvocation;
public void test3();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 24: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this Lcn/knightzz/jvm/bytecode/MethodInvocation;
public static void test4();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=0, locals=0, args_size=0
0: return
LineNumberTable:
line 27: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class cn/knightzz/jvm/bytecode/MethodInvocation
3: dup
4: invokespecial #3 // Method "":()V
7: astore_1
8: aload_1
9: invokespecial #4 // Method test1:()V
12: aload_1
13: invokespecial #5 // Method test2:()V
16: aload_1
17: invokevirtual #6 // Method test3:()V
20: aload_1
21: pop
22: invokestatic #7 // Method test4:()V
25: invokestatic #7 // Method test4:()V
28: return
LineNumberTable:
line 30: 0
line 31: 8
line 32: 12
line 33: 16
line 34: 20
line 35: 25
line 36: 28
LocalVariableTable:
Start Length Slot Name Signature
0 29 0 args [Ljava/lang/String;
8 21 1 d Lcn/knightzz/jvm/bytecode/MethodInvocation;
}
SourceFile: "MethodInvocation.java"
Main 函数中的调用字节码 :
stack=2, locals=2, args_size=1
0: new #2 // class cn/knightzz/jvm/bytecode/MethodInvocation
3: dup
4: invokespecial #3 // Method "":()V
7: astore_1
8: aload_1
9: invokespecial #4 // Method test1:()V
12: aload_1
13: invokespecial #5 // Method test2:()V
16: aload_1
17: invokevirtual #6 // Method test3:()V
20: aload_1 // 将对象引用放入操作数栈
21: pop // 但是静态方法不需要对象, 所以又把它从操作数栈移除了
22: invokestatic #7 // Method test4:()V
25: invokestatic #7 // Method test4:()V
28: return
new #2 : 是创建【对象】,给对象分配堆内存,执行成功会将【对象引用】压入操作数栈
dup 将栈顶的地址再次进行复制一份到操作数栈, 本例即为【对象引用】,这样操作数栈就有了两个对象引用,
一个是要配合 invokespecial 调用该对象的构造方法 " (会消耗掉栈顶一个引用)
另一个要配合 astore_1 赋值给局部变量 d (插槽1的变量)
最终方法(final),私有方法(private),构造方法都是由 invokespecial 指令来调用,属于静
态绑定
普通成员方法是由 invokevirtual 调用,属于动态绑定,即支持多态, 因为它在调用之前并不知道具体是哪个对象调用的, 可能是子类也可能是父类的
成员方法与静态方法调用的另一个区别是,执行方法前是否需要【对象引用】
比较有意思的是 d.test4(); 是通过【对象引用】调用一个静态方法
可以看到在调用invokestatic 之前执行了 pop 指令,把【对象引用】从操作数栈弹掉了
还有一个执行 invokespecial 的情况是通过 super 调用父类方法