• 类文件结构


    1、Java编译器将.java文件编译成为.class文件,实际上,是Java编译器读取源文件内容,经过一些列检查和分析后,整理成标准的、更方便Java虚拟机读取的字节码文件。

    2、在官方jdk中默认的Java编译器是javac.exe,虚拟机是java.exe,java.exe实际上包含了真正的虚拟机HotSpot。

    3、class文件是Java语言实现平台无关性、机器无关性和语言无关性的基石。

    平台无关性:class文件内容屏蔽了关于平台的差异,即不管是windows系统、Linux系统、还是其它支持的系统,class文件都不会因此有差异。

    机器无关性:class文件内容屏蔽了关于机器的差异,即不管是intel的x86指令架构或arm指令架构,不管是32位或者64位,class文件都不会因此有差异。

    语言无关性:class文件内容屏蔽了关于语言的差异,不管是c语言、python语言等,借助对应的编译器,class文件都可以正常实现和解读。

    类文件的结构

    目前,我不关心Java编译器是如何将Java源代码编译成字节码文件的,只关心编译后的字节码的内容。

    类型 名称 数量
    u4 magic 魔数 1
    u2 minor_version 次版本号 1
    u2 major_version 主版本号 1
    u2 constant_pool_count 常量池容量 1
    cp_info constan_pool 常量池 constant_pool_count
    u2 access_flags 访问标志 1
    u2 this_class 本类全限定名 1
    u2 super_class 父类限定名 1
    u2 interface_account 接口索引容量 1
    u2 interfaces 接口索引 interface_account
    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文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符。

    class文件中存储的数据类型分为两种:无符号数和表。

    无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2字节、4个字节和8个字节的无符号数。

    表由表项来组成,表项也可能是另一张表。

    魔数

    魔数是u4类型,即class文件的前4个字节的内容,作用是确定这个文件是否为一个被虚拟机接受的Class文件。魔数的默认值是0xCAFEBABE。

    次版本号和主版本号

    此版本号和主版本号都是u2类型,分别为class文件的第5、6个字节和第7、8个字节。

    Java的版本号是从45开始的,即主版本号都在45及之上。高版本的JDK能向下兼容以前的Class文件,但不能运行以后版本的Class文件,即使文件格式并未发生任何变化,虚拟机也必须拒绝执行超过其版本号的Class文件。

    常量池

    紧接着主次版本号之后的是常量池入口,由于常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项u2类型的数据,代表常量池容量计数值(constant_pool_count)。注意,该值是从1开始计数,当值为0x0016,即十进制为22,表示的范围是1~21,共21项常量。

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

    常量池中每一项常量都是一个表,在JDK1.7及之后有14种常量池项目类型,每一种项目都有特定的表结构。

    常量 描述 项目 类型 项目描述
    CONSTANT_Utf8_info UTF-8编码的字符串 tag u1 值为1
    length u2 UTF-8编码的字符串占用的字节数
    bytes u1 长度为length的UTF-8编码的字符串
    CONSTANT_Integer_info 整型字面量 tag u1 值为3
    bytes u4 按照高位在前存储的int值
    CONSTANT_Float_info 浮点型字面量 tag u1 值为4
    bytes u4 按照高位在前存储的float值
    CONSTANT_Long_info 长整型字面量 tag u1 值为5
    bytes u8 按照高位在前存储的long值
    CONSTANT_Double_info 双精度浮点型字面量 tag u1 值为6
    bytes u8 按照高位在前存储的double值
    CONSTANT_Class_info 类或接口的符合引用 tag u1 值为7
    index u2 指向全限定名常量项的索引
    CONSTANT_String_info 字符串类型字面量 tag u1 值为8
    index u2 指向字符串字面量的索引
    CONSTANT_Fieldref_info 字段的符号引用 tag u1 值为9
    index u2 指向声明字段的类或者接口描述符CONSTANT_Class_info的索引项
    index u2 指向字段描述符CONSTANT_NameAndType的索引项
    CONSTANT_Methodref_info 类中方法的符号引用 tag u1 值为10
    index u2 指向声明方法的类描述符CONSTANT_Class_info的索引项
    index u2 指向名称及类型描述符CONSTANT_NameAndType的索引项
    CONSTANT_InterfaceMethodref_info 接口中方法的符号引用 tag u1 值为11
    index u2 指向声明方法的接口描述符CONSTANT_Class_info的索引项
    index u2 指向名称及类型描述符CONSTANT_NameAndType的索引项
    CONSTANT_NameAndType_info 字段或方法的部分符号引用 tag u1 值为12
    index u2 指向该字段或方法名称常量项的索引
    index u2 指向该字段或方法描述符常量项的索引
    CONSTANT_MethodHandle_info 表示方法句柄 tag u1 值为15
    reference_kind u1 值必须在1~9范围,它决定了方法句柄的类型。方法句柄类型的值表示方法句柄的字节码行为
    reference_index u2 值必须是对常量池的有效索引
    CONSTANT_MethodType_info 标识方法类型 tag u1 值为16
    descriptor_index u2 值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示方法的描述符
    CONSTANT_InvokeDynamic_info 表示一个动态方法调用点 tag u1 值为18
    bootstrap_method_attr_index u2 值必须是对当前Class文件中引导方法表的bootstrap_methods[]数组的的有效索引
    name_and_type_index u2 值必须是对当前常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,表示方法名和方法描述符

    访问标志

    在常量池结束之后,紧接着的两个字节代表访问标志,这个标志用于识别 一些类或者接口层次的访问信息。

    标志名称 标志值 含义
    ACC_PUBLIC 0x0001 是否为public类型
    ACC_FINAL 0x0010 是否被声明为final类型,只有类可设置
    ACC_SUPER 0x0020 是否允许使用invokespecial字节码指令的新语意,invokespecial指令的语意在JDB1.2之后发生过改变,为了区别这条指令使用哪种语意,JDK1.0.2之后编译出来的类的这个标志都必须为真。
    ACC_INTERFACE 0x0200 标识这是一个接口
    ACC_ABSTRACT 0x0400 是否为abstract类型,对于接口或者抽象类来说,此标志为真,其它类值为假
    ACC_SYNTHETIC 0x1000 标识这个类并非由用户代码产生的
    ACC_ANNOTATION 0x2000 标识这是一个注解
    ACC_ENUM 0x4000 标识这是一个枚举

    类索引、父类索引和接口索引集合

    类索引、父类索引和接口索引集合都按顺序排列在访问标志之后,类索引和父类索引用两个u2类型的索引值表示,它们各自指向一个类型为CONSTANT_Class_info的类描述符常量,通过CONSTANT_Class_info类型的常量中的索引值找到可以定义在CONSTANT_Utf8_info类型的常量中的全限定名字符串。

    对于接口索引集合,入口的第一项——u2类型的数据为接口计数器,表示索引表的容量。如果该类没有实现任何接口,则该计数器值为0.后面的索引表不再占用任何字节。

    字段表集合

    在字段表集合的开头是一个u2类型的数据fields_count,表示字段表的个数。

    字段表用于描述接口或者类中声明的变量。字段包括类级变量和实例级变量,但不包括声明在方法内部声明的局部变量。

    字段表结构:

    类型 名称 数量
    u2 access_flags 1
    u2 name_index 1
    u2 descriptor_index 1
    u2 attributes_count 1
    attribute_info attributes attributes_count

    字段访问标志:

    标志名称 标志值 含义
    ACC_PUBLIC 0x0001 字段是否为public
    ACC_PRIVATE 0x0002 字段是否为private
    ACC_PROTECTED 0x0004 字段是否为protected
    ACC_STATIC 0x0008 字段是否为static
    ACC_FINAL 0x0010 字段是否为final
    ACC_VOLATILE 0x0040 字段是否为volatile
    ACC_TRANSIENT 0x0080 字段是否为transient
    ACC_SYNTHETIC 0x1000 字段是否由编译器自动产生的
    ACC_ENUM 0x4000 字段是否enum

    name_index和descriptor_index都是对常量池的引用,分别代表着字段的简单名称以及字段和方法的描述符。

    方法表集合

    在方法表集合的开头是一个u2类型的数据methods_count,表示方法表的个数。

    方法表结构与字段表结构一致:

    类型 名称 数量
    u2 access_flags 1
    u2 name_index 1
    u2 descriptor_index 1
    u2 attributes_count 1
    attribute_info attributes attributes_count

    访问标志与字段表的访问标志也很相似:区别在于volatile和transient不能修饰方法,但增加了synchronized、native、strictfp和abstract关键字。

    标志名称 标志值 含义
    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  方法是否是由编译器产生的桥接方法 
    ACC_VARARGS 0x0080  方法是否接受不定参数 
    ACC_NATIVE 0x0100  方法是否为native 
    ACC_ABSTRACT 0x0400  方法是否为abstract 
    ACC_STRICTFP 0x0800  方法是否为strictfp 
    ACC_SYNTHETIC  0x1000  方法是否是由编译器自动产生的 

     方法中的Java代码,经过编译器编译成字节码后,存放在方法属性表集合中一个名为“Code”的属性里面。

    属性表集合

     在方法表集合的开头是一个u2类型的数据attributes_count,表示属性表的个数。

    对于每个属性,它的名称需要从常量池中引用一个CONSTANT_Utf8_info类型的常量来表示,而属性值的结构则是完全自定义的,只需要通过一个u4的长度去说明属性值所占用的位数即可。

    属性表结构:

    类型 名称 数量
    u2 attribute_name_index 1
    u4 attribute_length 1
    u1 info attribute_length

    1、Code属性

    类型 名称 数量
    u2 attribute_name_index 1
    u4 attribute_length 1
    u2 max_stack 1
    u2 max_local 1
    u4 code_length 1
    u1 code code_length
    u2 exception_table_length 1
    exception_info exception_table exception_table_length
    u2 attributes_count 1
    attribute_info attributes attributes_count
  • 相关阅读:
    了解web前端基本常识
    解决移动端输入法挡住输入框的办法
    简单实现根据选项显示不同的表单
    关于“使用本服务器,放到正规的第三方服务器就不安全”的想法
    简单实现网页换肤功能
    Java项目引入eclipse注意事项
    hexo博客发布注意事项
    hexo博客出现“Cannot GET/xxxx”的错误
    C#中ToString()格式详解
    SQLSERVER 时间日期函数,查询今天日期、昨天、一个星期、半年前的数据
  • 原文地址:https://www.cnblogs.com/blunFan/p/11703759.html
Copyright © 2020-2023  润新知