java class文件内容之一

Java class文件是8位字节的二进制流。数据项按顺序存储在class文件中,相邻的项之间没有任何间隔,这样可以使class文件紧凑。占据多个字节空间的项按照高位在前的顺序分为几个连续的字节存放。和java的类可以包含多个不同的字段、方法、方法参数、局部变量等一样,Java class文件也能够包含许多不同大小的项。在class文件中,可变长度项的大小和长度位于其实际数据之前。这个特性使得class文件流可以从头到尾被顺序解析,首先读出项的大小,然后读出项的数据。

1.class文件内容

​ 1.1 java class文件基本类型

u11个字节,无符号类型
u22个字节,无符号类型
u44个字节,无符号类型
u88个字节,无符号类型

1.2 可变长度的ClassFile表中的项,如下表:

类型名称数量
u4magic1
minor_version1
major_version1
constant_pool_count1
cp_infoconstant_poolconstant_pool_count-1
u2access_flags1
this_class1
super_class1
interfaces_count1
interfacesinterfaces_count
fields_count1
field_infofieldsfields_count
methods_count1
methods1
attributes_count1
attribute_infoattributesattributes_count

1.3ClassFile表中各项含义如下:

1.3.1magic(魔数)

​ 每个java class文件的前4个字节被成为它的魔数:0XCAFEBABE 。魔数的作用在于,可以轻松的分辨出java class文件和非java class文件。如果一个文件不是以0XCAFEBABE开头,那么它就肯定不是java class文件。文件格式定义者能够自由选择魔数,前提是这个选定的魔数值没有被广泛引用。

1.3.2 minor_version和major_version

​ class 文件的下面4个字节包含了主、次版本号。对于Java虚拟机来说,版本号确定了特定的class文件格式,通常只有给定主版本号和一系列次版本号后,java虚拟机才能读取class文件。如果class文件的版本号超出了java虚拟机所能处理的有效范围,Java虚拟机将不会处理该class文件。

1.3.3constant_pool_count和constant_pool

​ 在class文件中,魔数和版本号后面的是常量池。常量池包含了与文件中类和接口相关的常量。常量池中存储了诸如文字字符串,final变量值、类名和方法名的常量。Java虚拟机把常量池组织为入口列表的形式。在实际列表constant_pool之前,是入口在列表着哦功能的计数constant_pool_count。

​ 常量池中的许多入口都指向其他的常量池入口,而且class文件中紧随着常量池的许多条目也会指向常量池中的入口。在整个class文件中,指示常量池入口在常量池列表中位置的整数索引都指向这些常量池入口。列表着哦功能的第一项索引值为1,第二项索引值为2,依此类推。尽管constant_pool列表中没有索引值为0的入口,但缺失的这一入口也被constant_pool_count计数在内。例如,当constant_pool中一14项(索引值从1到14时),constant_pool_count的值为15.

​ 每个常量池入口都从一个长度为一个字节的标识开始,这个标识指出了列表中该位置的常量类型。一旦java虚拟机获取并解析这个标识,java虚拟机就会知道在标识后的常量类型是什么。

​ 常量池标识

入口类型标识值描述
CONSTANT——Utf-81UTF-8编码的Unicode字符串
CONSTANT——Integer3int类型字面值
4float类型字面值
5long类型字面值
CONSTANT_Double6double类型字面值
7对一个类或接口的符号引用
CONSTANT_String8String类型字面值
9对一个字段的符号引用
10对一个类中声明的方法的符号引用
CONSTANT_InterfaceMethodref11对一个接口中声明的方法的符号引用
CONSTANT_NameAndType12对一个字段或方法的部分符号引用

​ 表中的每个标识都有一个相对应的表,表名通过在标识名后加上“_info”后缀来产生。例如,对应于CONSTANT_Class标识的表名为CONSTANT_Class_info,表名为CONSTANT_Utf8_info的表中存储着Unicode字符串的压缩形式。

​ 在动态链接的Java程序中,常量池充当了十分重要的角色。除了字面常量(或者说直接量)值以外,常量池还可以容纳下面几种符号引用:

(1)、类和接口的全限定名;

(2)、字段的名称和描述符;

(3)、方法的名称和描述符;

字段是类或接口的实例变量或者类变量。字段的描述符是一个指示字段的类型的字符串。方法的描述符也是一个字符串,该字符串指示方法的返回值和参数的数量、顺序和类型。在运行时,Java虚拟机使用常量池的全限定名、方法和字段的描述符,把当前类或接口中的代码与其他接口中的代码链接起来。由于class文件并不包含其内部组建最终内存布局的信息,因此类、字段和方法并不能被class文件中的字节码直接引用。Java虚拟机从常量池获得符号引用,然后在运行时解析引用项的实际地址。例如,用来调用方法的字节码指令把一个符号引用的常量池索引传给所调用的方法。

1.3.4access_flags

​ 紧接常量池后的两个字节称为access_flags,它展示了文件中定义的类或接口的几段信息。

​ ClassFile表内access_flags项的标识位

标识名设置后的含义设置者
0X0001public类型类和接口
0X0010类为final类型只有类
0X0020使用新型的invokespecial语义类和接口
0X0200接口类型,不是类类型所有的接口,没有类
0X0400abstract类型所有的接口,部分类

​ 在access_flags中所有未使用的位都必须由编译器置为0,而且Java虚拟机必须忽略它。

1.3.5this_class

​ 接下来的两个字节为this_class项,它是一个对常量池的索引。在this_class位置的常量池入口必须为CONSTANT_Class_info表。该表由两个部分组成——标签和name_index。标签部分是一个具有CONSTANT_Class值的常量,在name_index位置的常量池入口为一个包含子类或接口全限定名的CONSTANT_Utf8_info表。

​ this_class项提供了一个如何使用常量池的范例。对于它自身来说,this_class项只是一个指向常量池的索引。当java虚拟机在this_class位置查阅常量池的入口的时候,它会发现一个通过把自己的标签设为CONSTANT_Class来识别自身的项,Java虚拟机知道,在CONSTANT_Class_info入口中,标签的后面总是会有一个名为name_index的、指向常量池的索引,于是虚拟机在name_index位置查找常量池入口,在这个位置,Java虚拟机应该能找到一个容纳了类或者接口全限定名的CONSTANT_Utf8_info入口。对于这个过程,如下图示描述:

1.3.6super_class

​ 在class文件中,紧接在this_class之后的是super_class项,它是一个两个字节的常量池索引,在super_class位置的常量池入口是一个指向该类超类全限定名的CONSTANT_Class_info入口,因为Java程序中所有对象的基类都是java.lang.Object类,除了Object类以外,常量池索引super_class对于所有的类均有效。对于Object类,super_class的值为0,对于接口,在常量池入口super_class位置的项为java.lang.Object。

1.3.7interfaces_count和interfaces

​ 紧接着super_class的是interfaces_count。此项的含义为:在文件中由该类直接实现或由接口所扩展的父接口的数量。在这个计数的后面,是名为interfaces的数组,它包含了对每个由该类或者接口直接实现的父接口的常量池索引。每个父接口都使用一个常量池中的CONSTANT_Class_info入口来描述,该CONSTANT_Class_info入口指向接口的全限定名。这个数组只容纳哪些直接出现在类声明的implements子句或者接口声明的extends子句中的父接口。超类按照在implements子句和extends子句中出现的顺序(从左到右)在这个数组中显现。

1.3.8fields_count和fields

​ 在class文件中,紧接在interfaces后面的是对在该类或者接口中声明的字段的描述。首先是名为fields_count的计数,它是类变量和实例变量的字段的数量总和。在这个计数后面的是不同长度的field_info表的序列(fields_count指出了序列中有多少个field_info表)。只有在文件中由类或者接口声明了的字段才能在fields列表中列出。在fields列表中,不列出从超类或者父接口继承而来的字段。另一方面,fields列表可能会包含在对应的java源文件中没有叙述的字段,这时因为java编译器可能会在编译时向类或接口添加字段。例如,对于一个内部类的fields列表来说,为了保持对外围类实例的引用,java编译器会为每个外围类实例添加实例变量。源代码中并没有叙述任何在fields列表中的字段,它们是被java编译器在编译时添加进去的,这些字段使用synthetic属性标识。

​ 每个field_info表都展示了一个字段的信息。此表包含了字段的名字、描述符和修饰符。如果该字段被声明为final、field_info表还会展示其常量值。这样的信息有些放在field_info表中,有些则放在由field_info表所指向的常量池中。

1.3.9methods_count和methods

​ 在class文件中,紧接着fields后面的是对在该类或者接口中所声明的方法的描述。首先是名为methods_count的计数,它是一个双字节长度的对于该类或者接口中声明的所有方法的总计数。这个总计数只包括在该类或者接口中显式定义的方法(从超类或者父接口中继承来的方法不被计入)。在methods_count后面的是方法本身,它在一个method_info表的列表中进行了阐述(methods_count指出了列表中有多少个method_info表)。

​ method_info表中包含了与方法相关的一些信息,包括方法名和描述符(方法的返回值类型和参数类型)。如果方法既不是抽象的,又不是本地的,那么method_info表就包含局部变量所需的栈空间长度、为方法所捕获的异常表、字节吗序列以及可选的行数和局部变量标。如果方法能够抛出任何已验证的异常,那么method_info表就会包括一个关于这些已验证异常的列表。

1.3.10attributes_count和attributes

​ class文件中最后的部分是属性(attribute),它给出了在该文件中类或者接口所定义的属性的基本信息。属性部分由attriutes_count开始,attributes_count是指出现在后续attributes列表中的attribute_info表的数量总和。每个attribute_info的第一项是指向常量池中CONSTANT_Utf8_info表的索引,该表给出了属性的名称。

​ 属性有许多种。java虚拟机规范定义了几种属性,但任何人都可以创建他们自己的属性种类(通过特定的规则),并且把它们置于class文件中。java虚拟机实现必须忽略任何不能识别的属性。属性出现在class文件中的多处,而不仅仅在顶层ClassFile表的attributes项中出现。出现在ClasssFile表中的属性主要给出了与文件中所定义的类和接口相关的信息;出现在field_info表中的属性主要给出了与字段相关的信息;出现在method_info表中的属性主要给出了方法相关的信息。

其中java虚拟机实现定义了两种属性:SourceCode和InnerClasses。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值