Long long ago,在小编刚步入职场时,做了一个练手的小工具,能够展示一个图片及其相关的描述信息,并将所有信息保存在一个文件中,支持读取、修改操作,各位看官是不是立马就有实现思路了(如果没有思路,不妨停一下自己想想)。在写这篇文章时,不由的想起这个小工具,这个小工具实现思路和Class文件结构实现方式不能说完全一样,只能说一模一样。
总览
前面说过,Class文件是字节码的载体,而字节码(中间代码)是java语言无关平台的统一表示(诞生篇、骨架篇)。我们说Class文件结构,其实主要是说,字节码在Class文件中要如何表示。根据《Java虚拟机规范》规定,Class文件格式采用一种类似于C语言结构体的一种伪结构来存储数据,这种结构只有两种数据类型:无符号数和表。
无符号数:属于基本数据类型,用于表示数字、索引引用或按照UTF-8编码的字符串值;以u1、u2、u4、u8分别代表1个字节、2个字节、4个字节、8个字节的无符号数;
表:由多个无符号数或者其他表作为数据项构成的复合数据类型;便于区分,所有表都习惯以"_info"结尾;
整个Class文件也可以视为一张大表,其结构严格按照下表顺序定义,各个部分之间没有任何分割符。
| 类型 | 名称 | 数量 | 说明 |
|---|---|---|---|
| u4 | magic | 1 | 魔数,固定为0xCAFEBABE |
| u2 | minor_version | 1 | 次版本号 |
| u2 | major_version | 1 | 主版本号 (Java 8=52, 11=55, 17=61, 21=65) |
| u2 | constant_pool_count | 1 | 常量池大小 + 1 |
| cp_info | constant_pool | constant_pool_count - 1 | 常量池,类的资源仓库 |
| u2 | access_flags | 1 | 类/接口的访问标志 (public, final…) |
| u2 | this_class | 1 | 当前类在常量池的索引 |
| u2 | super_class | 1 | 父类在常量池的索引 (Object为0) |
| u2 | interfaces_count | 1 | 实现的接口数量 |
| u2 | interfaces | interfaces_count | 接口在常量池的索引集合 |
| u2 | fields_count | 1 | 字段表数量 |
| field_info | fields | fields_count | 字段表,描述所有字段 |
| u2 | methods_count | 1 | 方法表数量 |
| method_info | methods | methods_count | 方法表,描述所有方法 |
| u2 | attributes_count | 1 | 属性表数量 |
| attribute_info | attributes | attributes_count | 属性表,附加信息 |
真实的Class文件使用16进制工具打开后如下图所示,除了"CAFEBABE"之外,其他就完全不知道说啥,我们也完全没兴趣去一个个字节去翻译。来吧,上工具。。。

如下是经过jclasslib解析后的class文件结构,这就非常清晰了:

建议此处停顿下,将jclasslib解析的class结构信息和上述Class文件结构图一一对应看一下。
类型说明
魔数:Class文件的头4个字节,固定为"0xCAFEBABE",唯一的作用就是确定这个文件是否是一个能够被虚拟机接受的Class文件。
版本号:主次版本号共同决定了Class文件的版本。JVM提供向前兼容性,高版本的JVM可以运行低版本的Class文件,反之不行。《Java虚拟机规范》明确要求了即使文件格式未发生任何变化,虚拟机也必须拒绝执行超过其版本号的Class文件。Class文件版本号对应如下表:
| JDK 版本 | 发行年份 | Class 文件主版本号 (major_version) | 十六进制表示 |
|---|---|---|---|
| JDK 23 (早期访问版) | 2024 (预计) | 67 | 0x0043 |
| JDK 22 | 2024 | 66 | 0x0042 |
| JDK 21 (LTS) | 2023 | 65 | 0x0041 |
| JDK 20 | 2023 | 64 | 0x0040 |
| JDK 19 | 2022 | 63 | 0x003F |
| JDK 18 | 2022 | 62 | 0x003E |
| JDK 17 (LTS) | 2021 | 61 | 0x003D |
| JDK 16 | 2021 | 60 | 0x003C |
| JDK 15 | 2020 | 59 | 0x003B |
| JDK 14 | 2020 | 58 | 0x003A |
| JDK 13 | 2019 | 57 | 0x0039 |
| JDK 12 | 2019 | 56 | 0x0038 |
| JDK 11 (LTS) | 2018 | 55 | 0x0037 |
| JDK 10 | 2018 | 54 | 0x0036 |
| JDK 9 | 2017 | 53 | 0x0035 |
| JDK 8 (LTS) | 2014 | 52 | 0x0034 |
| JDK 7 | 2011 | 51 | 0x0033 |
| JDK 6 | 2006 | 50 | 0x0032 |
| JDK 5 | 2004 | 49 | 0x0031 |
| JDK 1.4 | 2002 | 48 | 0x0030 |
| JDK 1.3 | 2000 | 47 | 0x002F |
| JDK 1.2 | 1998 | 46 | 0x002E |
| JDK 1.1 | 1997 | 45 | 0x002D |
| JDK 1.0.2 | 1996 | 45 (及以下) | 0x002D |
常量池:是Class文件中最复杂、最重要的部分,是所有其他数据结构的引用来源。主要存放字面量和符号引用。核心作用是为了解耦,字节码指令、字段名、方法名等不直接存储具体的字符串,而是存储一个指向常量池的索引,使得字节码本身非常紧凑,也为动态链接提供了基础。

最低0.47元/天 解锁文章
803

被折叠的 条评论
为什么被折叠?



