Class类文件结构
注意一下讲解都是基于该Class文件结构;
任何一个Class文件都对应着一个类或者接口的定义信息;
Class文件是一组以8位字节为基础单位的二进制流;
Class文件存储结构:只有两种数据类型:无符号数和表
无符号数
无符号数属于基本数据类型:以u1、u2、u4、u8来代表一个字节、两个字节、三个字节、四个字节的无符号数
作用:可以用来描述数量值、索引引用、数字或者按照utf-8编码构成的字符串值;
表
是由无符号数和其他表作为数据项构成的复合数据类型,所有表都一般以"_info"结尾;
Class文件格式
1.魔数-Magic
Class文件的头四个字节成为魔数;
作用:确定这个Class文件能否被虚拟机接受
##2.Class文件版本号 ##
对应上图第二、三数据项:minor_version(次版本号) 、Major_Version(主版本号)
java版本号是从45开始的
3、常量池-Constant_pool
他是Class文件结构中和其他项目关联项目最多的数据类型,也是占用Class文件空间最大的数据项目之一;
由于常量池中的常量数量是不固定的,为此常量池入口需要放置一个u2数据类型的数据,代表常量池容量计数值(Constant_pool_count)
注意:常量池容量计数从1开始的比如:常量池计数对应的u2类型数据是0x0016对应的十进制为22,表示常量池中有22个常量(索引范围就是1-21);
常量池中主要用于存放两大类常量:字面量(比如文本字符串、声明为final的常量等)和符号引用
符号引用包含以下三类常量:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
常量池中每一项常量都是一个表;到JDK1.7共有14中表类型
这14中表结构都有一个共同点:表开始的第一位是一个u1类型的标志位(取值对应上图的标志列),用于判断该常量时哪种表类型;对应Class文件的结构图中(偏移量地址:0x0000000A)的0x07,即说明该常量时CONSTANT_Class_info类型的表,该常量是类或者接口符号引用;
以CONSTANT_Class_info表结构为例:
u1是上面我们说的标志位,name_index是一个索引值指向常量池中一个CONSTANT_String_info类型常量,此常量代表了一个这个类或者接口全限定名;即偏移地址(0x0000000B)为0x0002,换算成十进制2(表示指向常量中第2个常量),该常量的标志位即偏移地址:0X0000000D)为0x01,即标志为1,通过查看上面的表类型图说明该常量是CONSTANT_Uft8_info类型的常量;
5、访问标志
常量池结束后紧接着两个字节表示访问标志,用于标志类或者接口层次的访问信息包括:
- 这个Class是接口还是类
- 是否被定义为public类型
- 是否被定义为abstract类型
- 如果是类,是否被声明为final
具体标志位:

- 是实例变量还是那个静态成员变量
- 可变形(final)
- 并发可见性(volatile修饰,是否强制从主内存读写)
- 可否序列化(transient修饰符)
- 数据类型(基本类型、引用类型)
- 成员变量名称
这些通过一个u2类型的访问标志来实现(和类访问标志位大同小异);
字段表结构:
紧接着是两项索引值:name_index、descriptor_index都是对常量池的引用,表示字段名、字段描述符;
关于成员变量简单名称、类全限定名以及成员变量、方法描述讲解:
简单名:即变量名、方法名
类全限定名:把类全名如:java.lang.Object中的“.”换成了“/”java/lang/Object
成员变量描述符:描述变量类型
方法描述符:方法的参数列表(包括数量、类型、顺序)和返回值
基本数据类型(boolean、long除外)和无返回值的void类型用其首字母大写表示
对象类型使用大写L加类的全限定名
:如
数组的每一维度使用一个[表示如:
String[][]----->[[Ljava/lang/String
方法描述符如:
public char test(String[][] s,int b,long c,Int d){}
对应描述符为:([[Ljava/lang/StringIJI)C
字段表集合不会列出从超类或者父接口中继承而来的成员变量;
8、方法表集合
和字段表结构大同小异:
有点区别的就是访问标志:
方法中的代码经编译器编译成字节码指令后存放在属性表中的名为"Code"的属性里;
注意:父类的方法在子类中没有被重写,不会出现子类方法表集合中;
9、属性表集合
48)]
有点区别的就是访问标志:
[外链图片转存中…(img-kzt10Njt-1594971298850)]
方法中的代码经编译器编译成字节码指令后存放在属性表中的名为"Code"的属性里;
注意:父类的方法在子类中没有被重写,不会出现子类方法表集合中;