一、Class文件前言
1、概念:Class文件是以一组以8位字节为基础,各个数据按照严格的顺序格式排列的二进制流。当需要用到8字节以上空间的数据是,则会按照高位在前【Big-Endian】的方式分割成若干个8位字节进行存储。
2、不同语言可以在JVM上运行的本质:统一转化成class,JVM再去加载解析class,变成二进制文件让计算机执行
3、Class文件组成图(共16种)
二、Class文件详解
1、魔数
(1)固定值:固定值:ca fe ba be(十六进制)
(2)唯一作用:确定这个文件是否能被JVM解析
2、主次版本号:主次版本号和JDK的对应关系
3、常量池
(1)常量池的项目类型以及结构
(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)访问标识符
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、字段表结构对应图
b、字段表结构
c、字段访问标志
d、字段或方法返回值描述符
(2)其他知识点
a、字段表中不会列出从超类或者父类继承而来的字段,但有可能列出原本Java代码中不存在的字段,比如在内部类中会自动添加一个指向外部类实例的变量,用于访问外部类。
b、对于java代码来讲,两个字段只要名称重复就是非法的。但对字节码而言,只要两个字段的描述符不一致,那字段重名就是合法的。
c、如果属性数量为0,那么属性字段就不会出现。
d、在描述符标识字符中,引用类型后面有“;”。
9、方法表集合(methods_count/methods_info[])
(1)概述:方法表用于记录类方法相关的属性。
a、方法表结构对应关系
b、方法表结构
c、方法访问标志
(2)其他知识点
a、方法里面代码的存储:方法里面的Java代码经过编译成字节码指令后,存储在方法属性表集合中一个名为Code的属性里面。
b、重写:父类方法没有被子类重写,子类方法表集合中就不会有父类方法的信息,但同样可能出现由编译器自动添加的方法,最典型的就是类构造器方法“<clinit>()”和实例构造器方法“<init>”。
c、重载:Java中的重载要求方法简单名称相同且特征签名不同(返回值不包含在特征签名中)。JVM中的要求更广泛,只要描述符不完全一致的方法都可以共存。即使特征签名相同,如果返回值不同也可以共存于同一个class文件中。
10、属性表集合(attributes_count/attributes[])
(1)概述:属性表用于描述某些场景专有的信息,比如Class文件、字段表、方法表等的代码指令存储以及附属信息。属性表没有严格的顺序限制,只要不与已有的属性名重复即可。同时,JVM会忽略掉它不认识的属性。
(2)虚拟机规范预定义的属性(会因虚拟机版本变动)以及属性表结构
属性表结构
名称 | 类型 | 数量 |
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)描述符:用来描述方法或者字段的数据类型、方法参数列表(包括数量、类型以及顺序)和返回值。