class文件结构
java语言具有平台无关性,因为有jvm的支持,编译过后的java代码可以在任意平台上运行。java语言经编译后变成class文件,class文件在jvm上运行。现在jvm添加对其他语言的支持,其他语言编译成class文件也可以在jvm上运行,所以,jvm不仅提供了平台无关性,还提供了语言无关性。在jvm规范中定义了class文件格式,所有能够在jvm上运行的class文件必须要满足jvm定义的class文件规范,不是随随便便改个后缀名就能在jvm上运行,那jvm就危险了。
1.class文件结构
class文件是以8位字节流为基础的二进制流,文件中没有任何空格,是按照严格的顺序、长度排定的,每个位置都有特定含义,不允许修改。
class文件的内容是由俩种类型组成:
- 无符号数:u1,u2,u4,u8分别代表几个字节的无符号数,无符号数主要用来表示数字,索引,数值,字符值等
-
表:是复合类型,由多个无符号数或表组成
class文件是按照上表的顺序进行排定的,在其中某些类型的数据不一定时,通过一个前置计数器来确定数量。
2.魔数magic
标识符,相当于文件后缀名,只不过文件后缀名可以修改,是用来确认class文件能否被jvm接受。这可以避免随随便便修改文件后缀名就可以让jvm接受。
3.minor_version、major_version版本号
class文件版本号,高版本的jvm可以运行低版本的class文件,低版本jvm不能运行高版本的class文件。
4.常量池
常量池中的常量数量是不确定的,所以在常量池之前需要有一个常量计数器,用于记录常量池中的常量个数,这样在内存分配时,能够分配指定的空间给常量池。
常量池中主要存放的是字面量和符号引用。
字面量主要为文本字符串、声明为final的常量值等。
符号引用为类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。
下面是常量池中的项目类型,常量池中的每一项常量都是一个表,表的开始第一位是u1类型的标志位,表示常量属于哪种类型。
说明:在常量池中一些符号引用的索引值是指向constant_utf8_info类型的常量。class文件中方法、字段都需要引用constant_utf8_info型常量来描述名称,最大长度为u2,u2能表达的最大值是65535,所以在java程序中变量、方法名不能超过64kb。
5.访问标志
表示类或接口的访问信息,class文件是否为接口,是否定义为public类型,是否声明为final类型等等。
6.类索引、父类索引、接口
类索引、父类索引、接口记录了其全限定名的索引,指向常量池中的constant_class_info,再由constant_class_info指向constant_utf8_info找到对应的类全限定名。
由于一个类可以实现多个接口,因此,class文件中包含多个接口,此时需要一个前置计数器记录接口数量。
7.字段表集合
字段表存储类中的成员变量,包括实例变量和类变量,其结构如下:
access_flags:字段的访问标志,字段的类型,是否为public,是否为final,是否为volatile,是否为transient等。
name_index:字段名索引,最终指向constant_utf8_info中的字符串,代表字段的简单名称。
descriptor_index:字段描述符索引,用于描述字段的数据类型。基本数据类型的描述符用大写字母表示,如果字段是对象类型,用大写L表示,如果字段是数组类型,用[表示这是个数组。
attributes_count:属性表长度
attributes:存放字段的额外信息,比如说,如果定义为final类型的字段,初始化时由初始值,初始值存放在CONSTANT_VALUE属性中。
8.方法表集合
方法表与字段表类似,name_index描述方法的简单名,descriptor_index是方法的描述符,描述方法的参数,返回类型。对于方法中的方法体是由attributes中的code属性描述。
9.属性表集合
属性表主要用来描述字段表、方法表以外的一些额外信息