深入了解Class文件结构

本文详细介绍了Java Class文件的结构,包括平台无关性、字节码、常量池、访问标志、类索引、字段表、方法表和属性表集合等关键内容。讲解了魔数的作用、版本号的含义以及常量池的存储方式。此外,还提到了如何通过工具如jclasslib来直观查看和理解字节码。
摘要由CSDN通过智能技术生成

1.JVM的无关性

与平台无关性是建立在操作系统上,虚拟机厂商提供了许多可以运行在各种不同平台的虚拟机,它们都可以载入和执行字节码,从而实现程序的一次编写,到处运行https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节码(ByteCode)是构成平台无关性的基石,也是语言无关性的基础。Java 虚拟机不 和包括 Java 在内的任何语言绑定,它只与“Class 文件”这种特定的二进制文件格式所关联,Class 文件中包含了 Java 虚拟机指令集和符号表以及若干其他 辅助信息。

2.Class  类文件

Java 技术能够一直保持非常好的向后兼容性,这点 Class 文件结构的稳定性功不可没。Java 已经发展到 16 版本,但是 class 文件结构的内容,绝大部分在 JDK1.2 时代就已经定义好了。虽然 JDK1.2 的内容比较古老,但是 java 发展经历了十余个大版本,但是每次基本上只是在原有结构基础上新增内容、扩充 功能,并未对定义的内容做修改。
任何一个  Class  文件都对应着唯一一个类或接口的定义信息,但反过来说,Class  文件实际上它并不一定以磁盘文件的形式存在(比如可以动态生成、或者 直接送入类加载器中)。
Class  文件是一组以  8  位字节为基础单位的二进制流。Class  文件结构这些内容在面试的时候很少有人问。不过这个是自身内力提升的过程。

`/**`
 `* @author 三明治聊IT`
 `* @date 2021/6/12`
 `*/`
`public class Test {`
 `public Test() {`
 `}`
`}`

用  Sublime 这个工具打开以上Test.ava文件编译后的class
整个class文件的格式就是一个二进制的字节流。
各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符,这使得整个  Class  文件中存储的内容几乎全部是程序运行的必要数 据,没有空隙存在。
Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,这种伪结构中只有两种数据类型:无符号数和表。
无符号数属于基本的数据类型,以 u1、u2、u4、u8 来分别代表 1 个字节(一个字节是由两位 16 进制数组成)、2 个字节、4 个字节和 8 个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照  UTF-8  编码构成字符串值。
表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个 Class  文件本质上就是一张表。
接下来让我们详细解读class文件格式

2.1 魔数与Class文件的版本(magic: u4, minor_version: u2, major_version: u2)

每个 Class 文件的头 4 个字节称为魔数(Magic Number),它的唯一作用是确定这个文件是否为一个能被虚拟机接受的 Class 文件。使用魔数而不是扩展名来进行识别主要是基于安全方面的考虑,因为文件扩展名可以随意地改动。文件格式的制定者可以自由地选择魔数值,只要这个魔数值还没有被广泛采用过同时又不会引起混淆即可。
如下"cafe babe"就是我们class的魔数紧接着魔数的4个字节存储的是Class文件的版本号:第 5 和第 6 个字节是次版本号(MinorVersion),第 7 和第 8 个字节是主版本号(Major Version)。Java 的版本号是从 45 开始的,JDK 1.1 之后的每个 JDK 大版本发布主版本号向上加 1 高版本的 JDK 能向下兼容以前版本的 Class 文件,但不能运行以后版 本的 Class 文件,即使文件格式并未发生任何变化,虚拟机也必须拒绝执行超过其版本号的 Class 文件。

这里的"00 00 00 34" 代表JDK1.8(前面的"00 00"表示小版本是0。16进制的34,换成10进制就是52, 52-45+1=8,表示java8)

2.2 常量池(constant_pool_count: u2)

常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项 u2(2字节)类型的数据,代表常量池容量计数值(constant_pool_count)。与Java中语言习惯不一样的是,这个容量计数是从1而不是0开始的。
如下常量池数量是"0010", 十进制值是16,代表15项常量
由于u2类型数据表示2个字节,也就是16位。所以最大值只有2^16-1=65535。
所以我理解常量池的常量数量最多不能超过65535个。
常量池中主要存放两大类常量:
字面量(Literal)和符号引用(Symbolic References)。
字面量比较接近于 Java 语言层面的常量概念,如文本字符串、声明为 final 的常量值等。
符号引用则属于编译原理方面的概念,包括了下面三类常量:
类和接口的全限定名(Fully Qualified Name)、字段的名称和描述符(Descriptor)、方法的名称和描述符
接下来我们用javap反编译来解读Test.class的常量池
刚好是15项,跟我们常量池容量的计数值是一致的。
如果你想深入了解字节码,靠命令行还是多有不便的,这里推荐一个更加直观的工具jclasslib。
请搜索并安装以下插件。

然后打开的java文件 “Show Bytecode With Jclasslib”
然后就可以预览整个java文件编译后的class的字节码了

2.3 访问标志(access_flag:u2)

用于识别一些类或者接口层次的访问信息,包括:这个Class  是类还是接口;是否定义为public类型;是否定义为abstract  类型;如果是类的话,是否被声明为final等。访问标志在直接看字节码就不太好找了但是如果我们通过jclasslib, 就非常直观由图可知这是一个public类型的class。

2.4 类索引、父类索引与接口索引集合

类索引(this_class: u2): 该值必须是对常量池中某个常量的一个有效索引值,该索引处的成员必须是一个 CONSTANT_Class_info 类型的结构体,表示这个 class 文件所定义的类和接口
父类索引(super_class: u2)
接口计数器(interfaces_count: u2): 表示当前类或者接口直接继承接口的数量
接口表(interfaces: u2): 是一个表结构,成员同 this_class,是对常量池中 CONSTANT_Class_info 类型的一个有效索引值
当前测试类并无实现任何接口再看另一个类

2.5 字段表集合

字段计数器(fields_count: u2): 当前class文件所有字段的数量
字段表(fields): 是一个表结构,表中每个成员必须是 filed_info 数据结构,用于表示当前类或者接口的某个字段的完整描述,但它不包含从父类或者父接口继承的字段
描述接口或者类中声明的变量。字段(field)包括类级变量以及实例级变量。
而字段叫什么名字、字段被定义为什么数据类型,这些都是无法固定的,只能引用常量池中的常量来描述。
字段表集合中不会列出从超类或者父接口中继承而来的字段,但有可能列出原本  Java  代码之中不存在的字段,譬如在内部类中为了保持对外部类的访问 性,会自动添加指向外部类实例的字段。

2.6 方法表集合

方法计数器(methods_count: u2): 表示当前类方法表的成员个数
方法表(methods): 是一个表结构,表中每个成员必须是 method_info 数据结构,用于表示当前类或者接口的某个方法的完整描述
描述了方法的定义,但是方法里的 Java 代码,经过编译器编译成字节码指令后,存放在属性表集合中的方法属性表集合中一个名为“Code”的属性里面。与字段表集合相类似的,如果父类方法在子类中没有被重写(Override),方法表集合中就不会出现来自父类的方法信息。但同样的,有可能会出现由编译器自动添加的方法,最典型的便是类构造器“<clinit>”方法和实例构造器“<init>”

2.7 属性表集合

属性技术器(attributes_count: u2): 表示当前class文件attributes属性的成员个数
属性表(attributes): 是一个表结构,表中每个成员必须是 attribute_info 数据结构,这里的属性是对 class 文件本身,方法或者字段的补充描述,比如 SourceFile 属性用于表示 class 文件的源代码文件名
存储 Class 文件、字段表、方法表都自己的属性表集合,以用于描述某些场景专有的信息。如方法的代码就存储在 Code 属性表中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT三明治

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值