1 实例代码
接下来通过下面示例class文件示例来看了解JAVA虚拟机规范如何定义Class文件结构:
示例代码
public class TestClass {
private int m;
public int inc() {
return m + 1;
}
}
示例Class文件
2 访问标志(access_flags)
按照Java虚拟机规范紧跟在常量池表后面2个字节(u2)用来表示Class访问标志(access_flags),这个标志主要用于识别一些类或者接口层次的访问信息,主要包括:
- 这个Class是类还是接口;
- 是否定义为public类型;
- 是否定义abstract类型;
- 如果是类的话是否被声明为final;
Java虚拟机规范定义访问标志
示例中访问标志
访问标志2个字节来描述,对应16进制0x0021。而示例代码中TestClass是一个普通Java类,被public关键字修饰,因此它的ACC_PUBLIC、ACC_SUPER标志应为真,其余的标志位为假,它的 access_flags 值应为:0x0001|0x0020 = 0x0021,和示例值匹配。
对照源代码中定义类名称TestClass仅被public修饰
public class TestClass {
private int m;
public int inc() {
return m + 1;
}
}
10 类名,父类名,接口个数,接口名称
类名:用来表示类的全限定名
父类名:用来表示父类的全限定名
类接口个数:表示类的接口的数量
接口名称:表示类的接口名称
示例在Class文件2进制描述位置
- 类名用2位字节(u2)来描述,对应16进制0x0003,即十进制的3,3是常量池数组的一个下标,指向常量池中一个常量,通过对照javap中结果中对应到一个CONSTANT_Class_info类型的常量(下标#3),CONSTANT_Class_info类型常量其值转换为整数20,指向常量池中一个常量,通过对照javap中结果中对应到一个CONSTANT_Utf8_info类型的常量(下标#20),字符串值为"jvm/TestClass"。
- 父类名用2位字节(u2)来描述,对应16进制0x0004,即十进制的4,4是常量池数组的一个下标,指向常量池中一个常量,通过对照javap中结果中对应到一个CONSTANT_Class_info类型的常量(下标#4),CONSTANT_Class_info类型常量其值转换为整数21,指向常量池中一个常量,通过对照javap中结果中对应到一个CONSTANT_Utf8_info类型的常量(下标#21),字符串值为"java/lang/Object"。
- 类接口数量用2位字节(u2)来描述,对应16进制0x0000,即十进制的0,表示当前类没有实现接口。因此Class文件中不存在接口名称描述。
javap常量池结果
Constant pool:
#1 = Methodref #4.#18 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#19 // jvm/TestClass.m:I
#3 = Class #20 // jvm/TestClass
#4 = Class #21 // java/lang/Object
#5 = Utf8 m
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Ljvm/TestClass;
#14 = Utf8 inc
#15 = Utf8 ()I
#16 = Utf8 SourceFile
#17 = Utf8 TestClass.java
#18 = NameAndType #7:#8 // "<init>":()V
#19 = NameAndType #5:#6 // m:I
#20 = Utf8 jvm/TestClass
#21 = Utf8 java/lang/Object
对照源代码中定义类名称TestClass,继承父类java/lang/Object,没有实现接口
public class TestClass {
private int m;
public int inc() {
return m + 1;
}
}