1、Java编译器将.java文件编译成为.class文件,实际上,是Java编译器读取源文件内容,经过一些列检查和分析后,整理成标准的、更方便Java虚拟机读取的字节码文件。
2、在官方jdk中默认的Java编译器是javac.exe,虚拟机是java.exe,java.exe实际上包含了真正的虚拟机HotSpot。
3、class文件是Java语言实现平台无关性、机器无关性和语言无关性的基石。
平台无关性:class文件内容屏蔽了关于平台的差异,即不管是windows系统、Linux系统、还是其它支持的系统,class文件都不会因此有差异。
机器无关性:class文件内容屏蔽了关于机器的差异,即不管是intel的x86指令架构或arm指令架构,不管是32位或者64位,class文件都不会因此有差异。
语言无关性:class文件内容屏蔽了关于语言的差异,不管是c语言、python语言等,借助对应的编译器,class文件都可以正常实现和解读。
类文件的结构
目前,我不关心Java编译器是如何将Java源代码编译成字节码文件的,只关心编译后的字节码的内容。
类型 |
名称 |
数量 |
u4 |
magic |
魔数 |
1 |
u2 |
minor_version |
次版本号 |
1 |
u2 |
major_version |
主版本号 |
1 |
u2 |
constant_pool_count |
常量池容量 |
1 |
cp_info |
constan_pool |
常量池 |
constant_pool_count |
u2 |
access_flags |
访问标志 |
1 |
u2 |
this_class |
本类全限定名 |
1 |
u2 |
super_class |
父类限定名 |
1 |
u2 |
interface_account |
接口索引容量 |
1 |
u2 |
interfaces |
接口索引 |
interface_account |
u2 |
fields_count |
字段表容量 |
1 |
field_info |
fields |
字段表 |
fields_count |
u2 |
methods_count |
方法表容量 |
1 |
method_info |
methods |
方法表 |
methods_count |
u2 |
attributes_count |
属性表容量 |
1 |
attribute_info |
attributes |
属性表 |
attributes_count |
class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符。
class文件中存储的数据类型分为两种:无符号数和表。
无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2字节、4个字节和8个字节的无符号数。
表由表项来组成,表项也可能是另一张表。
魔数
魔数是u4类型,即class文件的前4个字节的内容,作用是确定这个文件是否为一个被虚拟机接受的Class文件。魔数的默认值是0xCAFEBABE。
次版本号和主版本号
此版本号和主版本号都是u2类型,分别为class文件的第5、6个字节和第7、8个字节。
Java的版本号是从45开始的,即主版本号都在45及之上。高版本的JDK能向下兼容以前的Class文件,但不能运行以后版本的Class文件,即使文件格式并未发生任何变化,虚拟机也必须拒绝执行超过其版本号的Class文件。
常量池
紧接着主次版本号之后的是常量池入口,由于常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项u2类型的数据,代表常量池容量计数值(constant_pool_count)。注意,该值是从1开始计数,当值为0x0016,即十进制为22,表示的范围是1~21,共21项常量。
常量池中主要存放两大类常量:字面量和符合引用。
常量池中每一项常量都是一个表,在JDK1.7及之后有14种常量池项目类型,每一种项目都有特定的表结构。
常量 |
描述 |
项目 |
类型 |
项目描述 |
CONSTANT_Utf8_info |
UTF-8编码的字符串 |
tag |
u1 |
值为1 |
length |
u2 |
UTF-8编码的字符串占用的字节数 |
bytes |
u1 |
长度为length的UTF-8编码的字符串 |
CONSTANT_Integer_info |
整型字面量 |
tag |
u1 |
值为3 |
bytes |
u4 |
按照高位在前存储的int值 |
CONSTANT_Float_info |
浮点型字面量 |
tag |
u1 |
值为4 |
bytes |
u4 |
按照高位在前存储的float值 |
CONSTANT_Long_info |
长整型字面量 |
tag |
u1 |
值为5 |
bytes |
u8 |
按照高位在前存储的long值 |
CONSTANT_Double_info |
双精度浮点型字面量 |
tag |
u1 |
值为6 |
bytes |
u8 |
按照高位在前存储的double值 |
CONSTANT_Class_info |
类或接口的符合引用 |
tag |
u1 |
值为7 |
index |
u2 |
指向全限定名常量项的索引 |
CONSTANT_String_info |
字符串类型字面量 |
tag |
u1 |
值为8 |
index |
u2 |
指向字符串字面量的索引 |
CONSTANT_Fieldref_info |
字段的符号引用 |
tag |
u1 |
值为9 |
index |
u2 |
指向声明字段的类或者接口描述符CONSTANT_Class_info的索引项 |
index |
u2 |
指向字段描述符CONSTANT_NameAndType的索引项 |
CONSTANT_Methodref_info |
类中方法的符号引用 |
tag |
u1 |
值为10 |
index |
u2 |
指向声明方法的类描述符CONSTANT_Class_info的索引项 |
index |
u2 |
指向名称及类型描述符CONSTANT_NameAndType的索引项 |
CONSTANT_InterfaceMethodref_info |
接口中方法的符号引用 |
tag |
u1 |
值为11 |
index |
u2 |
指向声明方法的接口描述符CONSTANT_Class_info的索引项 |
index |
u2 |
指向名称及类型描述符CONSTANT_NameAndType的索引项 |
CONSTANT_NameAndType_info |
字段或方法的部分符号引用 |
tag |
u1 |
值为12 |
index |
u2 |
指向该字段或方法名称常量项的索引 |
index |
u2 |
指向该字段或方法描述符常量项的索引 |
CONSTANT_MethodHandle_info |
表示方法句柄 |
tag |
u1 |
值为15 |
reference_kind |
u1 |
值必须在1~9范围,它决定了方法句柄的类型。方法句柄类型的值表示方法句柄的字节码行为 |
reference_index |
u2 |
值必须是对常量池的有效索引 |
CONSTANT_MethodType_info |
标识方法类型 |
tag |
u1 |
值为16 |
descriptor_index |
u2 |
值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示方法的描述符 |
CONSTANT_InvokeDynamic_info |
表示一个动态方法调用点 |
tag |
u1 |
值为18 |
bootstrap_method_attr_index |
u2 |
值必须是对当前Class文件中引导方法表的bootstrap_methods[]数组的的有效索引 |
name_and_type_index |
u2 |
值必须是对当前常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,表示方法名和方法描述符 |