Java Class文件结构
此文件格式为JAVA7的格式,可能与JAVA6 CLASS不一致。
每一个Class都对应着唯一的一个类或借口的定义信息。这里,我们称为"Class文件格式"只是通俗的将任意一个符合有效的类或借口的格式这么称呼,但
是它并不一定是以磁盘文件的形式存在。
每个Class文件都是由8字节为单位的字节流组成,所有的16位、32位和64位长度的数据将被构造成 2个、4个和8个8字节单位来表示。
ClassFile结构
每一个Class文件对应于一个如下所示的ClassFile结构体。
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count; field_info fields[fields_count];
u2 methods_count; method_info methods[methods_count];
u2 attributes_count; attribute_info attributes[attributes_count];
}
其中u1、u2、u4分别代表1、2、4个字节无符号数。
magic:
魔数,魔数的唯一作用是确定这个文件是否为一个能被虚拟机所接受的Class文件。魔数值固定为0xCAFEBABE,不会改变。
minor_version、major_version:
分别为Class文件的副版本和主版本。它们共同构成了Class文件的格式版本号。不同版本的虚拟机实现支持的Class文件版本号也相应不同,高版本号的虚拟机可以支持低版本的Class文件,反之则不成立。
constant_pool_count:
常量池计数器,constant_pool_count的值等于constant_pool表中的成员数加1。
constant_pool[]:
常量池,constant_pool是一种表结构,它包含Class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其它常量。常量池不同于其他,索引从1开始到constant_pool_count -1。
access_flags:
访问标志,access_flags是一种掩码标志,用于表示某个类或者接口的访问权限及基础属性。access_flags的取值范围和相应含义见下表:
this_class:
类索引,this_class的值必须是对constant_pool表中项目的一个有效索引值。constant_pool表在这个索引处的项必须为CONSTANT_Class_info类型常量,表示这个Class文件所定义的类或接口。
super_class:
父类索引,对于类来说,super_class的值必须为0或者是对constant_pool表中项目的一个有效索引值。如果它的值不为0,那 constant_pool表在这个索引处的项必须为CONSTANT_Class_info类型常量,表示这个Class文件所定义的类的直接父类。当 然,如果某个类super_class的值是0,那么它必定是java.lang.Object类,因为只有它是没有父类的。
interfaces_count:
接口计数器,interfaces_count的值表示当前类或接口的直接父接口数量。
interfaces[]:
接口表,interfaces[]数组中的每个成员的值必须是一个对constant_pool表中项目的一个有效索引值,它的长度为interfaces_count。每个成员interfaces[i] 必须为CONSTANT_Class_info类型常量。
fields_count:
字段计数器,fields_count的值表示当前Class文件fields[]数组的成员个数。
fields[]:
字段表,fields[]数组中的每个成员都必须是一个fields_info结构的数据项,用于表示当前类或接口中某个字段的完整描述。
methods_count:
方法计数器,methods_count的值表示当前Class文件methods[]数组的成员个数。
methods[]:
方法表,methods[]数组中的每个成员都必须是一个method_info结构的数据项,用于表示当前类或接口中某个方法的完整描述。
attributes_count:
属性计数器,attributes_count的值表示当前Class文件attributes表的成员个数。
attributes[]:
属性表,attributes表的每个项的值必须是attribute_info结构。
下面举个简单的例子来说明一下ClassFile的结构:
public class HelloWorld
{
String str = ""; public String getStr()
{
return str;
} public void setStr(String str)
{
this.str = str;
} }
通过javap工具我们能看到这个简单的类的结构,如下:
我们可以看到一些信息包括主副版本号、常量池、ACC_FLAGS等,再来打开Class文件看一下:
根据前面所述的ClassFile结构,我们来分析下:
可以看到前4个字节为魔数,也就是0xCAFEBABE,这里都是十六进制。
魔数后2个字节为副版本号,这里副版本号是0.
再后2个字节是主版本号0x0033,转为十进制,主版本号是51,和Javap工具所看到的一样,这里我用的JDK版本是1.7。
这两个字节是常量池计数器,常量池的数量为0x0017,转为十进制是23,也就是说常量池的索引为1~22,这与Javap所看到的也相符。
常量池计数器后面就是常量池的内容,我们根据javap所看到的信息找到最后一个常量池项java/lang/Object,在字节码中找到对应的地方:
常量池后面两个字节是访问标志access_flags:
值为0x0021,在javap中我们看到这个类的标志是
其中ACC_PUBLIC的值为0x0001,ACC_SUPER的值为0x0020,与字节码是相匹配的。
至于ClassFile的其他结构,包括this_class、super_class、接口计数器、接口等等都可以通过同样的方法进行分析,这里就不再多说了。
下面将详细的介绍一下ClassFile结构中的中的各个部分。
常量池
所有的常量池项都具有如下通用格式:
cp_info
{
u1 tag;
u1 info[];
}
以1个字节的tag开头,后面info[]项的内容tag由的类型所决定。tag有效的类型和对应的取值如下表:
下面我们来介绍下不同类型的tag所对应的结构和规则:
CONSTANT_Class_info结构:
CONSTANT_Class_info结构用于表示类或接口,格式如下:
CONSTANT_Class_info
{
u1 tag;
u2 name_index;
}
CONSTANT_Class_info结构的tag项的值为CONSTANT_Class(7)。name_index项的值,必须是对常量池的一个 有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info结构,代表一个有效的类或接口二进制名称的内部形式。
CONSTANT_Fieldref_info, CONSTANT_Methodref_info和CONSTANT_InterfaceMethodref_info结构:
字段,方法和接口方法由类似的结构表示:
CONSTANT_Fieldref_info
{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Methodref_info
{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_info
{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Fieldref_info结构的tag项的值为CONSTANT_Fieldref(9)。
CONSTANT_Methodref_info结构的tag项的值为CONSTANT_Methodref(10)。 CONSTANT_InterfaceMethodref_info结构的tag项的值为 CONSTANT_InterfaceMethodref(11)
class_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Class_info结构,表示一个类或接口,当前字段或方法是这个类或接口的成员。
name_and_type_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,它表示当前字段或方法的名字和描述符。
CONSTANT_String_info结构:
CONSTANT_String_info用于表示java.lang.String类型的常量对象,格式如下:
CONSTANT_String_info
{
u1 tag;
u2 string_index;
}
CONSTANT_String_info 结构的tag项的值为CONSTANT_String(8)。string_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是 CONSTANT_Utf8_info结构,表示一组Unicode码点序列,这组Unicode码点序列最终会被初始化为一个String对象。
CONSTANT_Integer_info和CONSTANT_Float_info结构:
CONSTANT_Intrger_info和CONSTANT_Float_info结构表示4字节(int和float)的数值常量:
CONSTANT_Integer_info
{
u1 tag;
u4 bytes;
}
CONSTANT_Float_info
{
u1 tag;
u4 bytes;
}
CONSTANT_Integer_info结构的bytes项表示int常量的值,按照Big-Endian的顺序存储。 CONSTANT_Float_info结构的bytes项按照IEEE 754单精度浮点格式。表示float常量的值,按照Big-Endian的顺序存储。
CONSTANT_Long_info和CONSTANT_Double_info结构:
CONSTANT_Long_info和CONSTANT_Double_info结构表示8字节(long和double)的数值常量:
CONSTANT_Long_info
{
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
CONSTANT_Double_info
{
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
在Class文件的常量池中,所有的8字节的常量都占两个表成员(项)的空间。如果一个CONSTANT_Long_info或 CONSTANT_Double_info结构的项在常量池中的索引为n,则常量池中下一个有效的项的索引为n+2,此时常量池中索引为n+1的项有效但 必须被认为不可用。
CONSTANT_Long_info结构的tag项的值是CONSTANT_Long(5)。 CONSTANT_Double_info结构的tag项的值是CONSTANT_Double(6)。
CONSTANT_Long_info结构中的无符号的high_bytes和low_bytes项用于共同表示long型常量,构造形式为 ((long) high_bytes << 32) + low_bytes,high_bytes和low_bytes都按照Big-Endian顺序存储。 CONSTANT_Double_info结构中的high_bytes和low_bytes共同按照IEEE 754双精度浮点格式表示double常量的值。high_bytes和low_bytes都按照Big-Endian顺序存储。
CONSTANT_NameAndType_info结构:
CONSTANT_NameAndType_info结构用于表示字段或方法,但是和前面介绍的三个表示字段方法的结构不同,CONSTANT_NameAndType_info结构没有标识出它所属的类或接口,格式如下:
CONSTANT_NameAndType_info
{
u1 tag;
u2 name_index;
u2 descriptor_index;
}
CONSTANT_NameAndType_info结构的tag项的值为CONSTANT_NameAndType(12)。
name_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,这个结构要么表示特殊的方法名<init>,要么表示一个有效的字段或方法的非限定名。
descriptor_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info(§4.4.7)结构,这个结构表示一个有效的字段描述符或方法描述符。
CONSTANT_Utf8_info结构:
CONSTANT_Utf8_info结构用于表示字符串常量的值:
CONSTANT_Utf8_info
{
u1 tag;
u2 length;
u1 bytes[length];
}
CONSTANT_Utf8_info结构的tag项的值为CONSTANT_Utf8(1)。length项的值指明了bytes[]数组的长度,bytes[]是表示字符串值的byte数组。
CONSTANT_MethodHandle_info结构:
CONSTANT_MethodHandle_info结构用于表示方法句柄,结构如下:
CONSTANT_MethodHandle_info
{
u1 tag;
u1 reference_kind;
u2 reference_index;
}
CONSTANT_MethodHandle_info结构的tag项的值为CONSTANT_MethodHandle(15)。reference_kind项的值必须在1至9之间(包括1和9),它决定了方法句柄的类型。
reference_index项的值必须是对常量池的有效索引,索引项和reference_kind的对应关系如下:
CONSTANT_MethodType_info结构:
CONSTANT_MethodType_info结构用于表示方法类型:
CONSTANT_MethodType_info
{
u1 tag;
u2 descriptor_index;
}
CONSTANT_MethodType_info结构的tag项的值为CONSTANT_MethodType(16)。 descriptor_index项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示方法的描 述符。
CONSTANT_InvokeDynamic_info结构:
CONSTANT_InvokeDynamic_info用于表示invokedynamic指令所使用到的引导方法、引导方法使用到动态调用名称、参数和请求返回类型、以及可以选择性的附加被称为静态参数的常量序列。
CONSTANT_InvokeDynamic_info
{
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
CONSTANT_InvokeDynamic_info结构的tag项的值为CONSTANT_InvokeDynamic(18)。 bootstrap_method_attr_index项的值必须是对当前Class文件中引导方法表的bootstrap_methods[]数组的 有效索引。ame_and_type_index项的值必须是对当前常量池的有效索引,常量池在该索引处的项必须是 CONSTANT_NameAndType_info结构,表示方法名和方法描述符。
下面我们还是使用上面ClassFile的例子来简单看下常量池:
通过javap我们看到常量池中第一项为:
是HelloWorld的初始化方法,再来看一下字节码:
0A是第一个常量池项的tag,转为十进制是10,查找上面的常量类型表的确是CONSTANT_Methodref类型常量。
根据CONSTANT_Methodref_info的结构,tag后2个字节为class_index,为常量池的某个可用索引,索引项必须为CONSTANT_Class_info结构:
0x0005,转为十进制是5。索引位置5的常量为:
可以看到是Object类,Java中所有的类都是Object类的子类。HelloWorld类没有显示的定义构造方法,会自动调用父类Object的无参构造方法。
继续看CONSTANT_Methodref_info的结构的第三个属性name_and_type_index,为常量池的某个可用索引,索引项必须为CONSTANT_NameAndType_info结构:
0x0012,转为十进制是18。索引位置18的常量为:
包括前面的索引5的CONSTANT_Class_info结构和这里的CONSTANT_NameAndType_info结构我们都可以继续追踪下去,这里我只是做简单分析就不再往下了。
用同样的方法可以分析常量池里每一个常量。
字段
每个字段(Field)都由field_info结构所定义,在同一个Class文件中,不会有两个字段同时具有相同的字段名和描述符。
field_info结构格式如下:
field_info
{
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
access_flags项的值是用于定义字段被访问权限和基础属性的掩码标志。取值范围如下表:
name_index项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示一个有效的字段的非全限定名。
descriptor_index项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示一个有效的字段的描述符。
attributes_count的项的值表示当前字段的附加属性的数量。
attributes表的每一个成员的值必须是attribute结构,一个字段可以有任意个关联属性。
方法
所有方法(Method),包括实例初始化方法和类初始化方法在内,都由method_info结构所定义。在一个Class文件中,不会有两个方法同时具有相同的方法名和描述符。
method_info结构格式如下:
method_info
{
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
access_flags项的值是用于定义当前方法的访问权限和基本属性的掩码标志,取值范围如下表:
标记名 值 说明
ACC_PUBLIC 0x0001 public,方法可以从包外访问
ACC_PRIVATE 0x0002 private,方法只能本类中访问
ACC_PROTECTED 0x0004 protected,方法在自身和子类可以访问
ACC_STATIC 0x0008 static,静态方法
ACC_FINAL 0x0010 final,方法不能被重写(覆盖)
ACC_SYNCHRONIZED 0x0020 synchronized,方法由管程同步
ACC_BRIDGE 0x0040 bridge,方法由编译器产生
ACC_VARARGS 0x0080 表示方法带有变长参数
ACC_NATIVE 0x0100 native,方法引用非java语言的本地方法
ACC_ABSTRACT 0x0400 abstract,方法没有具体实现
ACC_STRICT 0x0800 strictfp,方法使用FP-strict浮点格式
ACC_SYNTHETIC 0x1000 方法在源文件中不出现,由编译器产生
name_index项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info结构。
descriptor_index项的值必须是对常量池的一个有效索引。常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示一个有效的方法的描述符。
attributes_count的项的值表示这个方法的附加属性的数量。attributes表的每一个成员的值必须是attribute结构,一个方法可以有任意个与之相关的属性。
属性:
属性(Attributes)在Class文件格式中的ClassFile结构、field_info 结构,method_info结构和Code_attribute结构都有使用,所有属性的通用格式如下:
attribute_info
{
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
对于任意属性,attribute_name_index必须是对当前Class文件的常量池的有效16位无符号索引。常量池在该索引处的项必须是 CONSTANT_Utf8_info结构,表示当前属性的名字。attribute_length项的值给出了跟随其后的字节的长度,这个长度不包括 attribute_name_index和attribute_name_index项的6个字节。
对于字段、方法、和属性的结构,我们很容易的可以通过javap工具查看到。
Java Class文件结构的更多相关文章
- 《深入理解java虚拟机》笔记——简析java类文件结构
一直不太搞得明确jvm究竟是如何进行类载入的,在看资料的过程中迷迷糊糊.在理解类载入之前,首先看看java的类文件结构究竟是如何的,都包含了哪些内容. 最直接的參考当然是官方文档:The Java® ...
- Java 类文件结构
Java 诞生之时有句著名的宣传口号"Write Once, Run Anywhere.".但是,Java 语言本身不具备跨平台的能力,而是 JVM 提供了跨平台的能力. 事实上, ...
- Java类文件结构详解
概述: Class文件结构是了解虚拟机的重要基础之一,如果想深入的了解虚拟机,Class文件结构是不能不了解的.Class文件是一组以8位字节为基础单位的二进制流,各项数据项目严格按照顺序紧凑地排列在 ...
- 不知道Java类文件结构的同学,看这篇文章就够了
一.前言 代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步.经过多年的发展,目前的计算机仍然只能识别0和1,但是由于近10年内虚拟机以及大量建立在虚拟机之上的程 ...
- Java之Java的文件结构(才不是叛教!)
Java从入门到恰饭之文件结构,使用IDEA开发. 新建包 包名一般选择公司域名(https://tieba.baidu.com/)的反转 创建完成是这样的 对应的三层文件夹 我们创建一个类 pack ...
- java类文件结构笔记
注:新的博客地址 - https://zhengw-tech.com/archives/ 我们都知道java实现跨平台靠的是虚拟机技术,将源文件编译成与操作系统无关的,只有虚拟机能识别并执行的字节码文 ...
- Java类文件结构
一.概述 实现语言无关性的基础是虚拟机和字节码存储格式.Java虚拟机不和包括Java在内的任何语言绑定,只与"Class文件"这种特定的二进制文件所关联,Class文件中包含了J ...
- Java类文件结构及javac的ClassReader类解读
首先来看一下ClassFile,类注释如下: A JVM class file. Generic Java classfiles have one additional attribute for c ...
- Eclipse 平台Java项目文件结构
随机推荐
- 如何打印枚举类型:%d
#include <stdio.h> typedef enum SessionState { SESSION_OPENING, /* Session scope is being crea ...
- 浅谈冒烟测试(Smoke Testing)
鉴于之前跟开发提到提测前需要进行冒烟测试,然后几个开发一脸迷茫的问我:什么叫冒烟测试?所以我准备跟大家从以下几个方面简单的分享一下什么是冒烟测试. 一.软件测试的分类 二.冒烟测试(Smoke Tes ...
- HDU 1222 Wolf and Rabbit( 简单拓欧 )
链接:传送门 题意:狼抓兔子,狼从 0 出发沿逆时针寻找兔子,每走一步的距离为 m ,所有洞窟的编号为 0 - n-1 ,问是否存在一个洞窟使得兔子能够安全躲过无数次狼的搜捕. 思路:简单的拓展欧几里 ...
- 小试牛刀之sort()排序的实现
受大学室友的鼓动,我也打算利用公众平台来记录自己的前端知识积累,同时呢,自己总结的东西,总归会有局限性,希望小伙伴能给我指点迷津.知识就是一张巨大的网,作为一名摸不清头绪的入学者,唯一能做的事情就是吐 ...
- python_形参、实参
#参数:形参.实参'''def display_message(title): print("My favourite book is %s" %title) #return 0 ...
- 20121124.Nodejs异步式I/O与事件式编程
异步: 你请人吃饭,准备一起去的.结果那人刚好有事,让你先去点菜,你去点好菜,他忙完就来了,这就是异步的优势(不耽误事!)同步: 就是,你必须等那个人忙完了,才一起去(浪费时间) 理解来源于群友&qu ...
- BCB使用线程删除目录中的图片
BCB新建线程DeleteImgThread类.其会默认继承Thread类,然后在Execute函数中编写代码, void __fastcall DeleteImgThread::Execute() ...
- 怎样获取ios设备的唯一标识
非常多地方都会须要用到唯一标志. 比方: 1. 我们相用一个设备的唯一标志当作用户id,特别是网络游戏,这样就能够省去注冊的麻烦. 2. 想把app相关的文件加密,密钥哪里来的?有些人可能会说hard ...
- Revolution Platform
Revolution Platform 黑暗的极权统治现实 异类的处境 独孤的存在 觉者的形成 信仰的确立 信仰的产物 完整的思想理论 反抗与信仰的一致 反抗的超理性的智慧论 反抗的纯理性的方法论 反 ...
- 归并排序(Python)
一.采用分治策略:将原问题划分成n个规模较小的但结构和原问题相同的子问题,递归解决这些子问题后合并各个结果从而得到原问题的解. 二.分治策略的步骤: 分解:将原问题分解成一系列子问题 解决:子问题粒度 ...