每个Class文件的头4个字节称为魔数(Magic Number),作用是确定这个文件是否为一个能被虚拟机接受的Class文件。很多文件存储标准中都是用魔数来进行身份识别。Class文件的魔数值为:0xCAFEBABE。 第五和第六个字节是次版本号,第7和第8为主版本号。
由于常量池中常量的数量不固定的,所以在常量池的入口需要放置一项u2类型的数据,代表常量池容量计数器(constant_pool_count)。
常量池主要存放量大类常量:字面量(Literal)和符号引用(Symbolic References)。字面量比较接近于Java语言岑冕的常量概念,如文本字符串、声明为final的常量等。而符号引用则属于编译原理方面的概念,包括了下面三类常量:
类和接口的全限定名(Fully Qualified Name)
字段名称和描述符(Descriptor)
方法的名称和描述符
javap -verboss TestsClass可以输出常量列表
在常量池结束之后,紧接着的两个字节代表访问标志(access_flags),这个标志用于识别一些类或接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final等。
类索引用于确定这个类的全限定名;
父类索引用于确定这个类的父类的全限定名,由于Java语言不允许多重继承,所以父类索引只有一个,除了java.lang.Object之外,所有的Java类都有父类,因此除了java.lang.Object外,所有Java类的父类索引都不为0;
接口索引集合就用来描述这个类实现了哪些接口,这些被实现的接口将按implements语句后的接口顺序从左到右排列在接口索引集合中。
字段表(field_info)用于描述接口或者勒种声明的变量。字段(field)包括类级变量以及实例变量,但不包括在方法内部声明的局部变量。可以包括的信息有:字段的作用域(public、private、protected)、实例变量还是类变量(static修饰符)、可变性(final)、并发可见性(volatile修饰符,是否强制从主存读写)、可否被序列化(transient修饰符)、字段数据类型(基本类型、对象、数组)、字段名称。上述这些信息中,各个修饰符都是布尔类值,非有即无。
方法表的结构如同字段表一样,一次包括了方位标志(access_flags)、名称索引(name_index)、描述符索引(descriptor_index)、属性表集合(attributes)。
属性表(attribute_info)在Class文件、字段表、方发表都可以携带自己的属性表集合,以用于描述某些场景专有的信息。
Code属性:字节码指令存储在Code属性内。
Exceptions属性:是在方法表中与Code属性平级的一项属性,作用是列举出方法中可能抛出的受查异常(Checked Exceptions),也就是描述时在throws关键字后面雷剧的异常。
LineNumberTable属性:用于描述Java源码行号与字节行号(字节码的偏移量)之间的对应关系。
LocalVariableTable属性:用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系。
SourceFile属性:用于记录生成这个Class文件的源码文件名称。
ConstantValue属性:通知虚拟机自动为静态变量复制。只有被static关键字修饰的变量(类变量)才可以使用这项属性。
InnerClasses属性:用于记录内部类与宿主类之间的关联。
Deprecated以及Synthetic属性:Deprecated和Synthetic两个属性都属于标志类型属性的布尔属性,只存在有何没有的区别,没有属性概念。Deprecated属性用于标识某个类、字段、方法,已经被程序坐着定位不再推荐使用,它可以通过代码中使用@Deprecated注释进行设置。
StackMapTable属性:他是一个复杂的变长属性,位于Code属性的属性表中。这个属性会在虚拟机类加载的字节码验证阶段被新类型检查验证器使用。
Signature属性:它是一个可选的定长属性,可以出现于类、属性表和方法结构的属性表中。在JDK1.5中大幅增强了Java语言的语法,再次之后,任何类、接口、初始化方法或成员的泛型签名如果包含了类变量或参数化类型,则Signature属性会为他记录泛型签名信息。
BootstrapMethods属性:在JDK1.7之后增加到了Class文件规范之中,他是一个复杂的变长属性,位于类的属性表中。这个属性用于保存invokedynamic指令引用的引导方法限定符。
获取更多干货请关注公众号 lanxue-it