欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

class类文件结构

发布时间:2025/3/20 编程问答 27 豆豆
生活随笔 收集整理的这篇文章主要介绍了 class类文件结构 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

目录

  • Class 类文件的结构
  • 幻数和Class文件版本
  • 常量池
  • 访问标志
  • 类索引
  • 父类索引
  • 接口索引集合
  • 字段表集合
    • 访问标识符
    • 简单名和描述符
  • 方法表集合
    • 属性表
  • class文件属性表
  • 脚注
  • 参考资料

Class 类文件的结构

由java源码编译后的class文件包含了Java虚拟机指令和符号表以及若干其他辅助信息。class文件是一组以8位比特(1字节)为单位的二进制流,按照大端顺序存储,严格按照顺序依次排列下去。

class文件采用类似c语言结构体的伪结构存储数据,这种结构只有两种数据类型,无符号数和表。

无符号数是基本数据类型,可以表示整型、浮点型和字符串字面值等。用u1,u2,u4,u8分别表示1个字节,2个字节,4个字节和8个字节的无符号数。

表是由多个无符号数和其他表组合成的复合类型。表类型一般以_info结尾。

整个class文件结构如下表格1

类型名称数量
u4magic1
u2minor_version1
u2major_version1
u2constant_pool_count1
cp_infoconstant_poolconstant_pool_count-1
u2access_flags1
u2this_class1
u2super_class1
u2interfaces_count1
u2interfacesinterfaces_count
u2fields_count1
field_infofieldsfields_count
u2methods_count1
method_infomethodsmethods_count
u2attributes_count1
attribute_infoattributesattributes_count

class文件开始处四个字节表示magic,紧接着两个字节表示minor_version和两个字节表示major_version,紧接着两个字节表示常量池数目,紧接着是常量池数目-1个不同结构的常量表,依次类推。

写一个简单的程序,编译后分析字节码。

源码:

package com.hnzhrh;public class Simple{private int sum = 0;public int add(int a,int b){sum = a + b;return sum;} }

得到的class文件以十六进制查看:

幻数和Class文件版本

class文件格式表格第一行,u4类型的magic一个。

0x0000+0~0x0000+3:CA FE BA BE

该值表示该文件是能被JVM接收的class文件。使用幻数区分文件类型而不使用文件后缀名,文件后缀名是可以任意更改的(把jpg改成txt照常还能用画图打开,使用幻数区分)。

class文件格式表格第二行,u2类型的minor_version一个。

0x0000+4~0x0000+5:00 00

表示次版本号为0。

class文件格式表格第三行,u2类型的major_version一个。

0x0000+6~0x0000+7:00 34

表示主版本号为52。

常量池

常量池主要存放两大类常量:字面量和符号引用。

符号引用包括了下面三种常量:

  • 类和接口的全限定名
  • 字段的名称
  • 字段的描述符
  • 方法的名称
  • 方法的描述符

class文件格式表格第四行,u2类型的constant_pool_count一个。

0x0000+8~0x0000+9:00 13

表示常量池中常量计数,数值为19。但常量项有18个,该计数是从1开始的 。空出来的第0项用来满足特定情况下不引用常量池项目的情况,可以指向常量池0项。

紧接着常量计数之后,就是18个常量项,每个常量项都有自己独立的表结构,表结构的开头tag标志表明了常量类型,如下表2所示:

0x0000+10:0A

查看表可知,tag标志为10(0x0A)的常量类型是CONSTANT_Methodref_info,该类型大小为5字节,所以有:

0x0000+10~0x0000+14 为一个CONSTANT_Methodref_info类型常量,也是一个表结构。

通过表查看CONSTANT_Methodref_info类型的结构,该结构是由u1类型的tag,u2类型的index(指向声明方法的类描述符CONSTANT_Class_info的索引项)和u2类型的index(指向名称及类型描述符CONSTANT_NameAndType的索引项)组成的。

将0x0000+10~0x0000+14划分成三部分内容:

0x0000+10:0A u1类型的tag,值为10。

0x0000+11~0x0000+12:00 04 u2类型的index,值为4。

0x0000+13~0x0000+14:00 0F u2类型的index,值为15。

为了直观,建立一个表格,#+数字表示常量池项的索引,后面单元格存放常量项结构的内容。如下所示:

常量池标号(index)
#0
#110#4#15

其余常量项跟上述类似,最后得到的完整的常量池的直观表格:

可读这一列的分析以#1的常量项为例:

至此,常量池的内容结束(0x0000+8~0x0160+5)。

访问标志

class文件格式表格第6行,u2类型的access_flags一个。

0x0160+6~0x0160+7:00 21

访问标志用来表示这个class是类还是接口,是否定义为public,是否定义为abstract等。具有多个属性则将多个标志值或运算得到的结果作为访问标志。

访问标志表3

标志名称标志值含义
ACC_PUBLIC0x0001是否为public
ACC_FINAL0x0010是否为final,只有类可设置
ACC_SUPER0x0020JDK 1.0.2之后编译出来的类的这个标志为真
ACC_INTERFACE0x0200标识是接口
ACC_ABSTRACT0x0400是否为abstract,接口和抽象类为真
ACC_SYNTHETIC0x1000标识这个类并非由用户代码产生
ACC_ANNOTATION0x2000标识这是一个注解
ACC_ENUM0x4000标识这是一个枚举

该class访问标志为0x 0021,表示是一个public类。

类索引

class文件格式表第7行,u2类型的this_class一个。

0x0160+8~0x0160+9:00 03

类索引为0x0003,即指向常量池#3,是一个Class_info类型的常量,表示类或接口的符号引用,该常量的index指向#17,即com/hnzhrh/Simple。

类索引用来确定该类的全限定名。

父类索引

class文件格式表第8行,u2类型的super_class一个。

0x0160+10~0x0160+11:00 04

父类索引为Ox0004,即指向常量池#4,同上分析,得到java/lang/Object。

父类索引用来确定这个类的父类的全限定名。

接口索引集合

class文件格式第9行,u2类型的interface_count一个。

0x0160+12~0x0160+13:00 00

表示接口计数器,该值为0,则该类没有实现接口。所以class文件格式第10行的interfaces也没有。

类索引,父类索引和接口索引集合三者可以表明该类或接口的继承关系。

字段表集合

class文件格式第11行,u2类型的fields_count一个。

0x0160+14~0x0160+15:00 01

表示有1个字段表。

字段表不像常量项那样各有各的结构,字段表的结构是统一的,如下表4所示:

类型名称数量
u2access_flags1
u2name_index1
u2descriptor_index1
u2attributes_count1
attribute_infoattributesattributes_count

类似上述分析方法,得到如下可读表格:

类型名称数量值可读
u2access_flags10x00 02private
u2name_index10x00 05sum
u2descriptor_index10x00 06I
u2attributes_count10x00 00
attribute_infoattributesattributes_count

字段类型用于描述接口或者类中声明的变量。

访问标识符

access_flags可以表明字段所具有的特性,access_flags由下表5所示:

标志名称标志值含义
ACC_PUBLIC0x0001是否为public
ACC_PRIVATE0x0002是否为private
ACC_PROTECTED0x0004是否为protected
ACC_STATIC0x0008是否为static
ACC_FINAL0x0010是否为final
ACC_VOLATILE0x0040是否为volatile
ACC_TRANSIENT0x0080是否为transient
ACC_SYNTHETIC0x1000是否由编译器自动生成
ACC_ENUM0x4000是否为enum

简单名和描述符

name_index和descriptor_index两者都为常量池的索引。

name_index为字段的简单名索引(即不带有包名的名称)。

描述符的作用是用来描述字段的数据类型,方法的参数列表(包括数量、类型和顺序)和返回值。

基本数据类型和表示无返回值的void的描述符都用一个大写字母来表示,对象类型则用L加对象的全限定名表示,如下表6

描述符含义
Bbyte
Cchar
Ddouble
Ffloat
Iint
Jlong
Sshort
Zboolean
Vvoid
L对象类型

对于数组类型,使用一个前置的"["来描述,比如定义一个java.lang.String[][]类型的二维数组,将被记录为[Ljava/lang/String;。

字段表集合中不会列出从超类或父接口中继承而来的字段,但有可能列出原本Java代码中不存在的字段,比如在内部类中为了保持对外部类的访问性,会自动添加指向外部类实例的字段。

方法表集合

class文件格式第13行,u2类型的methods_count一个。

0x0176+8~0x0176+9:00 02

表示有两个方法表。

方法表结构与字段表结构一致,如下表7

类型名称数量
u2access_flags1
u2name_index1
u2descriptor_index1
u2attributes_count1
attribute_infoattributesattributes_count

方法访问标志与字段访问标志略有不同,如下表8:

标志名称标志值含义
ACC_PUBLIC0x0001是否为public
ACC_PRIVATE0x0002是否为private
ACC_PROTECTED0x0004是否为protected
ACC_STATIC0x0008是否为static
ACC_FINAL0x0010是否为final
ACC_SYNCHRONIZED0x0020是否为synchronized
ACC_BRIDGE0x0040是否为编译器产生的桥接方法
ACC_VARARGS0x0080是否接受不定参数
ACC_NATIVE0x0100是否为native
ACC_ABSTRACT0x0400是否为abstract
ACC_STRICTFP0x0800是否为strictfp
ACC_SYNTHETIC0x1000是否由编译器自动生成

如果父类方法没有被重写,方发表中不会出现来自父类的方法,也有可能出现编译器自动添加的方法,如类构造器<clinit>和实列构造器<init>方法。

开始分析方法表集合:

0x0176+10~0x0176+11:00 01

该方法是public。

0x0176+12~0x0176+13:00 07

简单名索引为7,即<init>。

0x0176+14~0x0176+5:00 08

描述符索引为8,即V()。

0x0192+0~0x0192+1:00 01

属性个数为1。

接下来就是一个属性表。

属性表

在class文件、字段表和方法表都可以携带属性表集合,用于描述某些场景专有的信息。

属性表开头都是u2类型的属性名称索引和u4类型的属性长度,紧接着就是不同属性的结构了。

属性表中可能包含其他属性表。

紧接着上述分析:

0x0192+2~0x0192+3:00 09

属性名称索引为9,即Code属性。

0x0192+4~0x0192+7:00 00 00 26

该属性的长度为38字节。

Code属性表9结构:

类型名称数量
u2attribute_name_index1
u4attribute_length1
u2max_stack1
u2max_locals1
u4code_length1
u1codecode_length
u2exception_table_length1
exception_infoexception_tableexception_table_length
u2attributes_count1
attribute_infoattributesattributes_count

同上分析,可以得到:
max_stack:2,max_locals:1

max_stack代表了操作数栈深度的最大值,虚拟机运行时根据这个值来分配栈帧中操作数栈大小。

max_locals代表了局部变量表所需的存储空间,在这里是隐式传递给方法的this参数。

code_length:10

表示字节码长度。

code:10字节长的字节码指令,2A B7 00 01 2A 03 B5 00 02 B1

exception_table_length:0

attributes_count:1

表示Code属性中还有1个属性。

紧接着就是这个属性的名称索引和长度,名称索引:10,即LineNumberTable,长度:10。

LineNumberTable属性用于描述Java源行号和字节码偏移量之间的对应关系。

LineNumberTable属性表10结构:

类型名称数量
u2attribute_name_index1
u4attribute_length1
u2line_number_table_length1
line_number_infoline_number_tableline_number_table_length

line_number_info表包含了两个u2类型的数据项,第一个表示字节码行号,第二个表示Java源码行号。

line_number_table_length : 2

line_number_infor: 0 : 3 和 4 : 4。

至此,<init>方法结束。

开始分析下一个方法,分析方法类似,分析结果如下表:

类型名称值说明
u2access_flags0x00 01public
u2name_index0x00 0Badd
u2descriptor_index0x00 0C(II)I
u2attributes_count0x00 011个属性
u2attribute_name_index0x00 09Code
u4attribute_length0x00 00 00 28属性长度40字节
u2max_stack0x00 03
u2max_locals0x00 03
u4code_length0x00 00 00 0C字节码长度12字节
u1code2A 1B 1C 60 B5 00 02 2A B4 00 02 AC字节码
u2exception_table_length0x00 00
exception_infoexception_table
u2attributes_count0x00 01Code属性的属性
u2attribute_name_index0x00 0ALineNumberTable
u4attribute_length0x00 00 00 0A属性长度10字节
u2line_number_table_length0x00 02有两个line_number_info
u2start_pc0x00 00字节码偏移量
u2line_number0x00 06源码行号
u2start_pc0x00 07
u2line_number0x00 07

class文件属性表

在class文件、字段表和方法表都可以携带属性表集合,用于描述某些场景专有的信息。

属性表开头都是u2类型的属性名称索引和u4类型的属性长度,紧接着就是不同属性的结构了。

属性表中可能包含其他属性表。

class文件格式表格第15行,u2类型的attributes_count一个。

0x0288+4~0x0288+5:00 01

表示class文件有1个属性表。

分析方法类似,结果如下:

类型名称值说明
u2attribute_name_index0x00 0DSourceFile
u4attribute_length0x00 00 00 02属性长度2字节
u2sourcefile_index0x00 0ESimple.java

脚注

参考资料

深入理解Java虚拟机 JVM高级特性与最佳实践 周志明著 第二版


  • 表格内容来源于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-1↩

  • 表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-3和表6-6↩

  • 表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-7↩

  • 表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-8↩

  • 表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-9↩

  • 表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-10↩

  • 表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-11↩

  • 表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-12↩

  • 表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-15↩

  • 表格内容来源于于《深入理解Java虚拟机 JVM高级特性与最佳实践》第2版 周志明著 第六章表6-18↩

  • 转载于:https://www.cnblogs.com/RohanZhang/p/9510863.html

    总结

    以上是生活随笔为你收集整理的class类文件结构的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。