一、Class文件前言

1、概念:Class文件是以一组以8位字节为基础,各个数据按照严格的顺序格式排列的二进制流。当需要用到8字节以上空间的数据是,则会按照高位在前【Big-Endian】的方式分割成若干个8位字节进行存储。

2、不同语言可以在JVM上运行的本质:统一转化成class,JVM再去加载解析class,变成二进制文件让计算机执行

ab包 bytes文件_描述符

 

3、Class文件组成图(共16种)

 

ab包 bytes文件_jvm_02

二、Class文件详解

1、魔数

(1)固定值:固定值:ca fe ba be(十六进制)

(2)唯一作用:确定这个文件是否能被JVM解析

2、主次版本号:主次版本号和JDK的对应关系

ab包 bytes文件_jvm_03

 

3、常量池

(1)常量池的项目类型以及结构

ab包 bytes文件_字段_04

 

(2)常量池的分类

a、class中的常量池:静态的

b、运行时常量池:动态的

c、字符串常量池:StringTable

(3)常量池存储内容

a、字面常量[Literal]:文本字符串、声明为final的常量值等等;

b、符号引用[Symbolic References]:类和接口的全限定名、字段的名称和描述符(如:B、L/java.util.String;等等)、方法的名称和符号描述(如:()V等等)

(4)注意

a、Java程序中如果定义的方法名或者变量名超过64K将无法编译;

b、由于常量池的index是从1开始的,所以常量池的大小是constant_pool_count -1;

c、常量池中的index为0的下标用于表示不引用任何一个常量

4、访问标志

(1)作用:用于标识一些类或者接口层次的访问信息,包括8个标识符,以下列几点:

a、这个Class是类还是接口;

b、是否定义为public;

c、是否定义为abstract;

d、如果是类的话,是否被声明final等等

(2)访问标识符

ab包 bytes文件_ab包 bytes文件_05

 

5、类名【类索引】

类索引是u2类型的数据,指向一个类型为CONSTANT_Class_info的类描述符常量,通过CONSTANT_Class_info的常量中的索引可以找到定义在CONSTANT_Utf8_info类型的常量的全限定名字符串。

6、父类名【父类索引】

父类索引是u2类型的数据,指向一个类型为CONSTANT_Class_info的类描述符常量,通过CONSTANT_Class_info的常量中的索引可以找到定义在CONSTANT_Utf-8_info类型的常量的权限定名字符串。除了Object对象外,所有的类的父类索引值均不为0。

7、接口【接口索引】

(1)概述:接口索引是一组u2类型的数据的集合,接口索引用来描述这个类实现了哪些接口,这些被实现/继承的接口将按照implements/extends顺序从左到右排列在接口索引集合中。

(2)结构:接口索引入口的第一项是一个u2类型的数据,表示接口数目(最大值为65535,最小值为0)。如果接口数目为0,则后面的接口索引表不占任何字节,即interface[]为空。

8、成员属性【字段表集合field_info】

(1)概述:字段表集合用于描述接口或类中声明的变量,字段包括类级变量以及实例级变量,但不包括在方法内部声明的局部变量。

a、字段表结构对应图

ab包 bytes文件_常量池_06

 

b、字段表结构

ab包 bytes文件_ab包 bytes文件_07

 

c、字段访问标志

ab包 bytes文件_描述符_08

 

d、字段或方法返回值描述符

ab包 bytes文件_常量池_09

 

(2)其他知识点

a、字段表中不会列出从超类或者父类继承而来的字段,但有可能列出原本Java代码中不存在的字段,比如在内部类中会自动添加一个指向外部类实例的变量,用于访问外部类。

b、对于java代码来讲,两个字段只要名称重复就是非法的。但对字节码而言,只要两个字段的描述符不一致,那字段重名就是合法的。

c、如果属性数量为0,那么属性字段就不会出现。

d、在描述符标识字符中,引用类型后面有“;”。

9、方法表集合(methods_count/methods_info[])

(1)概述:方法表用于记录类方法相关的属性。

a、方法表结构对应关系

ab包 bytes文件_jvm_10

 

b、方法表结构

ab包 bytes文件_ab包 bytes文件_11

 

c、方法访问标志

ab包 bytes文件_常量池_12

 

(2)其他知识点

a、方法里面代码的存储:方法里面的Java代码经过编译成字节码指令后,存储在方法属性表集合中一个名为Code的属性里面。

b、重写:父类方法没有被子类重写,子类方法表集合中就不会有父类方法的信息,但同样可能出现由编译器自动添加的方法,最典型的就是类构造器方法“<clinit>()”和实例构造器方法“<init>”。

c、重载:Java中的重载要求方法简单名称相同且特征签名不同(返回值不包含在特征签名中)。JVM中的要求更广泛,只要描述符不完全一致的方法都可以共存。即使特征签名相同,如果返回值不同也可以共存于同一个class文件中。

10、属性表集合(attributes_count/attributes[])

(1)概述:属性表用于描述某些场景专有的信息,比如Class文件、字段表、方法表等的代码指令存储以及附属信息。属性表没有严格的顺序限制,只要不与已有的属性名重复即可。同时,JVM会忽略掉它不认识的属性。

(2)虚拟机规范预定义的属性(会因虚拟机版本变动)以及属性表结构

ab包 bytes文件_jvm_13

 

属性表结构

名称

类型

数量

attribute_name_index

u2

1

attribute_length

u4

1

info

u1

attribute_length

三、属性表集合详解

1、Code属性

(1)作用:Java程序方法体经过javac编译后最终变为字节码存储在Code属性中。

2、Exceptions属性

(1)作用:列举方法中可能抛出的受查异常(Check Exceptions),也就是方法描述时在throws关键字后面列举的异常。

3、LineNumberTable属性

(1)作用:描述Java源码的行号和字节码行号(偏移量)之间的关系。它不是运行时的必需属性,默认会生成到Class文件之中。

4、LocalVariableTable属性

(1)作用:描述Java源码中定义的变量和栈帧中局部变量表中的变量之间的关系。它不是运行时的必需属性,默认会生成到Class文件之中。

5、SourceFile属性

(1)作用:用于记录生成这个Class文件的源码文件名称。可选属性。

6、ConstantValue属性

(1)作用:通知虚拟机为static修饰的变量赋值。实例变量会在<init>方法中赋值;被final修饰的静态基本类型或String类型数据,会生成ConstantValue属性初始化;未被final修饰或者并非基本类型或String类型的静态量,会在<clinit>方法中进行初始化。

7、InnerClass属性

(1)作用:记录内部类与宿主之间的关联。

8、Deprecated属性

(1)作用:布尔属性,表示某个类、方法、字段已被废弃,对应java源码中的@deprecated注释。

9、Synthetic属性

(1)作用:代表方法或字段不是由Java源码直接产生的。

9、StackMapTable属性

(1)作用:位于Code属性中的一个复杂变长属性。在加载字节码验证阶段被类新型检查验证器(Type Checker)使用。

10、Signature属性

(1)作用:记录泛型签名信息。它出现于类、方法、属性表中,是一个定长属性。

11、BootstrapMethods属性

(1)作用:位于类文件属性中的变长属性。用于保存invokedynamic指令引用的引导方法限定符。

 

 

N、其他

1、大端与小端

(1)大端:高地址存储数据的低位,低地址存储数据的高位

(2)小端:高地址存储数据的高位,低地址存储数据的低位

2、字节码相关

(1)Linux下解析class的命令:hexdump

3、变量相关概念

(1)类级变量:全局级变量或静态变量,需要使用static关键字修饰,可以直接被类访问。

(2)实例级变量:成员变量,实例化后才会分配内存空间。

(3)方法级变量:局部变量,方法内部定义的变量。

(4)块级变量:定义在一个块内部的变量,生命周期就是所在的这个块,比如if、for循环语句中的变量。

4、全限定名、简单明、描述符

(1)全限定名:类所属的包路径+类名。

(2)简单名:没有类型和参数修饰的方法或者字段名称。比如"public static void main(String[] arg)"的简单名是“main”。

(3)描述符:用来描述方法或者字段的数据类型、方法参数列表(包括数量、类型以及顺序)和返回值。