tags
type
status
date
slug
summary
category
password
icon
本文以 JDK8为例,讲解 Class 文件结构。
JDK 文档官网:docs.oracle.com/javase/8/
前言
Oracle 有两个产品实现了 Java SE (Java Platform Standard Edition) 8,分别为Java SE Development Kit (JDK) 8和 Java SE Runtime Environment (JRE) 8。
JDK 8是 JRE 8的超集,包含 JRE 8中的所有内容,以及开发 applet 和应用程序所需的编译器和调试器等工具。
而 JRE 8提供库、Java 虚拟机(JVM)和其他组件来运行用 Java 编程语言编写的 applet 和应用程序。请注意,JRE 包括 Java SE 规范不需要的组件,包括标准组件和非标准组件。
简而言之,JDK 适用于开发,JRE 仅仅适用于 applet 和应用程序的部署运行,及在服务器环境下可以只使用 JRE。
class 文件的产生
.class
文件是.java
文件编译后的形成的中间文件。下面使用一个小例子来看一下 class 文件的结构。
例:我们有以下的源代码文件——
Main.java
编译
javac Main.java ---> Main.class
补充点编译细节:Main.java -> 词法分析器 -> tokens 流 -> 语法分析器 -> 语法树/抽象语法树 -> 语义分析器 -> 注解抽象语法树 -> 字节码生成器 -> Main.class 文件。这里就不展开细说啦。
经过编译后,我们得到了
Main.class
文件,如果要打开这个文件我们会看到一串一串字符乱码,这些字符是无法直接取阅读的。通过反编译后,就可以得到类似于源码的代码,如下。补充:反编译工具
javap
。
- IDE 工具:如 Idea 工具栏中 View---'show bytecode'。
- Idea 插件——
jclasslib Bytecode Viewer
,最直观展示信息。
javap
是 Java 开发工具包(JDK)提供的一个命令行工具,用于反编译 Java 字节码。javap
可以将 Java 类文件解析为易于阅读的文本形式,展示其中的信息以及反编译出类的结构、方法、字段、常量池等信息。通过阅读和分析这些信息,开发人员可以更好地理解 Java 类的内部实现,并进行性能调优、代码审查等操作。我们可以使用
jclasslib Bytecode Viewer
插件,直观的阅读 class 文件结构,包括 class 文件格式的主次版本号、常量池等信息。class
文件结构介绍
每个
class
文件包含一个类或接口的定义。类文件由字节流组成,既我们所看到的一串一串字符。所有 16 位、32 位和 64 位的变量也都是通过读入 2 个、4 个和 8 个连续的字节构建的。并且多字节数据项总是按大端模式存储,即高字节位在前。
如下是 oracle 官方给的 class 文件结构
案例中的数据类型表示为
u1
、u2
和 u4
类型分别代表一个、两个或四个字节的无符号量,这些类型可通过 java. Io. DataInput 接口的 readUnsignedByte、readUnsignedShort 和 readInt 等方法读取。下面介绍下 class 文件结构的具体含义。
类型 | 名称 | 数量 | 说明 | ㅤ |
u4 | magic | 1 | 魔数:确定一个文件是否是 Class 文件。值是固定的为 0xCAFEBABE | ㅤ |
u2 | minor\_version | 1 | Class 文件的次版本号,与主版本号共同决定 class 文件格式的版本。如果一个类文件的主要版本号为 M,次要版本号为 m,我们就用 M.m 表示其类文件格式的版本。 | ㅤ |
u2 | major\_version | 1 | Class 文件的主版本号:一个 JVM 实例只能支持特定范围内版本号的 Class 文件(可以向下兼容)。 | ㅤ |
u2 | constant\_pool\_count | 1 | 常量表数量,constant\_pool\_count 项的值等于 constant\_pool 表中的条目数加1。如果一个常量大于0且小于 constant\_pool\_count 则被认为是有效的。 | ㅤ |
cp\_info | constant\_pool | constant\_pool\_count-1 | 常量池:可以理解为 Class 文件的资源仓库,表示在ClassFile结构及其子结构中引用的各种字符串常量、类和接口名、字段名和其他常量。后面的其他数据项可以引用常量池内容。 | ㅤ |
u2 | access\_flags | 1 | 类的访问标志信息:用于表示这个类或者接口的访问权限及基础属性。 | ㅤ |
u2 | this\_class | 1 | 指向当前类的常量索引:用来确定这个类的的全限定名。 | ㅤ |
u2 | super\_class | 1 | 指向父类的常量的索引:用来确定这个类的父类的全限定名。 | ㅤ |
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 | 属性表数量 | ㅤ |
attributes\_info | attributes | attributes\_count | 属性表集合:用于描述某些场景专有的信息,如字节码的指令信息等等。 | ㅤ |