类文件结构【Class类文件的结构】

概述

任何一个Class文件都对应着唯一一个类或接口的定义信息,但反过来,类或接口并不一定都得定义在文件里(如类或接口可通过类加载器直接生成)。下文只是通俗地将任意一个有效的类或接口所应当满足的格式称为“Class文件格式”,实际上它并不一定以磁盘形式存在。

Class文件

一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件中,中间没有任何分割符(使得存储的内存几乎全部是程序运行的必要数据,没有空隙存在)。当遇到需要占用8位字节以上空间的数据项时,则会按照高位在前的方式切割成若干个8位字节进行存储。

高位在前(Big-Endian)
具体是指最高位字节在地址最低位、最低位字节在地址最高位的顺序来存储数据,它是SPARC、PowerPC等处理器的默认多字节存储顺序,而x86等处理器则是使用了相反的“Little-Endian”顺序来存储数据。
如short a = 0x3132;(0x31是高字节,0x32是低字节)
short a 的变量地址:


高位及低位在前.png

Class文件格式

Java虚拟机规范规定,Class文件格式采用类似C语言结构体的伪结构来存储数据,只有两种数据类型:无符号数和表。

无符号数
  • 数据基本数据类型,以u1(1个字节)、u2(2个字节)、u4(4个字节)、u8(8个字节)分别代表无字符数。
  • 可用来描述数字、索引引用、数量值或者按照UTF-8编码构成的字符串值。
  • 由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以“_info”结尾。

  • 表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表。


    Class文件格式.png
  • 无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的数据项的形式,这时称这一系列连续的某一类型数据为某一类型的集合。

  • Class结构没有任何分隔符,无论顺序还是数量,甚至于数据存储的字节序(Byte Ordering,Class文件中字节序为Big-Endian)这样的细节,都被严格限定,哪个字节代表什么含义,长度是多少,先后顺序如何,都不允许改变。

一、魔数与Class文件的版本

  • 每个Class文件的头4个字节称为魔数(Magic Number),它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件。(0xCAFEBABE)
  • 紧接着魔数的4个字节存储的是Class文件的版本号:第5和第6个字节是次版本号(Minor Version),第7和第8个字节是主版本号(Major Version)。Java的版本是从45开始的。高版本JDK能向下兼容以前版本的Class文件,但不能运行,即使文件格式并未发生改变,虚拟机也必须拒绝执行超过其版本号的Class文件。


    魔数与版本号.png
  • Class文件版本号


    Class文件版本号.png

二、常量池

  • 紧跟着主次版本号的是常量池入口,常量池可理解为Class文件之中的资源仓库。它是Class文件结构中与其他项目关联最多的数据类型,也是占用空间最大的数据之一,它还是第一个出现的表类型数据项目。
  • 常量池常量数量不固定,所以在入口需放置一项u2类型的数据,代表常量池容量计数值(constant pool count)。此计数器从1开始计数。如0x0016(十进制的22),代表索引值为1-22的21项常量。之所以把0空出来,是为了满足后面某些指向常量池的索引值的数据在特定情况下需表达“不引用任何一个常量池项目”的含义。Class文件结构中,只有常量池的容量计数是从1开始的。


    常量池中的计数.png
  • 常量池存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。
    1. 字面量
      接近于Java层面的常量概念。如文本字符串、声明为final的常量值等。
    2. 符号引用
      属于编译原理方面的概念,包括下面三类常量。
      • 类和接口的权限定名(Fully Qualified Name)
      • 字段的名称和描述符(Descriptor)
      • 方法的名称和描述符
  • 虚拟机加载Class文件时进行动态连接。即在Class文件中不会保存各个方法、字段的最终内存布局信息,因此这些字段、方法的符号引用不经过运行期转换的话无法得到真正的入口地址,也就无法直接被虚拟机使用。当虚拟机运行时,需从常量池获得对应的符号引用,再在类创建或运行时解析、翻译到具体的内存地址中。
  • 常量池每一项常量都是一个表,在JDK 1.7之前共有11种结构,在JDK 1.7中为更好的支持动态语言调用,又额外增加了3种(CONSTANT_MethodHandle_info、CONSTANT_MethodType_info和CONSTANT_InvokeDynamic_info)。
  • 14种表都有共同的特点,就是表开始的第一位是一个u1类型的标志位(tag,取值见下图),代表当前这个常量属于哪种常量类型。


    常量池的项目类型.png

    常量池中的14种常量项的结构总表.png
  • 解析
    如0x07属于CONSTANT_Class_info,tag代表了标志位,index是一个索引值,它指向了常量池中的一个CONSTANT_Utf8_info类型常量,代表了这个类(或接口)的全限定名,也即指向常量池中第二个常量。
    标志位0x01代表CONSTANT_Utf8_info类型常量,length说明这个UTF-8的字符串长度是多少字节,它后面紧跟着的长度为length字节的连续数据是一个使用UTF-8缩略编码表示的字符串。

UTF-8缩略编码与普通UTF-8编码的区别:从'\u0001'到'\u007f'之间的字符(1-127的ASCII码)的缩略编码使用一个字节表示,从'\u0080'到'\u07ff'之间的所有字符的缩略编码用两个字节表示,从‘\u0800’到'\uffff'之间的所有字符的缩略编码按照普通UTF-8编码规则的三个字节表示。

Class文件中的方法、字段都需引用CONSTANT_Utf8_info型常量描述名称,CONSTANT_Utf8_info的最大长度(u2,65535,64KB)也就是Java中方法、字段名的最大长度。

  • JDK的bin中,有工具javap 专门用于分析Class文件字节码。


    javap分析Class文件字节码.png

    从清单可看出有一些常量没在代码中出现,如“I”、“V”、“”、“LineNumberTable”、“LocalVariableTable”等,这部分自动生成的常量会被字段表(field_info)、方法表(method_info)、属性表(attribute_info)引用到,它们会用来描述一些不方便使用“固定字节”进行表示的内容。如描述方法的返回值是什么?有几个参数?每个参数的类型是什么?因Java中的“类”是无穷无尽的,无法通过简单的无符号字节来描述一个方法用到了什么类,因此在描述方法的这些信息时,需要引用常量表中的符号引用来进行表达。

三、访问标志

  • 常量池结束之后,紧跟着两个字节表示代表访问标志(access_flags),这个标志用于识别一些类或接口层次的访问信息,包括:Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类,是否被声明为final等。


    访问标识.png

access_flags中共有16个标志位可以使用,当前只定义了8个,没有使用到的标志位要求一律为0。
如public 修饰的普通类,他的ACC_PUBLIC和ACC_SUPER为真,即access_flags为0x0001|0x0020=0x0021。


access_flags标志.png

四、类索引、父类索引与接口索引集合

  • 类索引(this_class,用于确定这个类的全限定名)和父类索引(super_class,用于确定这个类的父类的全限定名,父类索引只有一个,因为不允许多重继承)都是一个u2类型的数据,而接口索引集合(interfaces,用来描述这个类实现了哪些接口,这些被实现的接口将按implements语句,如果是接口则是extends语句后的接口顺序从左到右排列在接口索引集合中)是一组u2类型的数据的集合,Class文件由这三项数据来确定这个类的继承关系
  • 类索引、父类索引和接口索引集合都按顺序排列在访问标志之后,类索引和父类索引用两个u2类型的索引值表示,各自指向一个类型为CONSTANT_Class_info的类描述符常量,而类描述符常量中的索引值可找到定义在CONSTANT_Utf8_info类型中的全限定名字符串。


    类索引查找全限定名的过程.png
  • 对于接口索引集合,第一项u2类型的数据为接口计数器(interface_count),表示索引表的容量。若该类没实现任何接口,则该计数器值为0,后面的接口的索引表不再占用任何字节。


    类索引、父类索引、接口索引集合.png

    部分常量池内容.png

五、字段表集合

  • 字段表(field_info)用于描述接口或者类中声明的变量。
  • 字段(field)包括类级变量一级实例变量,但不包括在方法内部声明的局部变量。
  • Java中描述对象包含的信息。
    1. 字段的作用域(public、private、protected修饰符)
    2. 是实例变量还是类变量(static修饰符)
    3. 可变性(final)
    4. 并发可见性(volatile修饰符,是否强制从主内存读写)
    5. 可否被序列化(transient修饰符)
    6. 字段数据类型(基本类型、对象、数组)
    7. 字段名称


      字段表结构.png
字段访问标志.png
  • 全限定名
    如“org/fenixsoft/clazz/TestClass”是一个全限定名,把类全名中的“.”替换为“/”,为了使多个全限定名之间不产生混淆,以“;”表示全限定名结束。

  • 简单名称
    指没有类型和参数修饰的方法或字段名称,如“inc()”为“inc”,m字段为“m”。

  • 方法和字段的描述符
    复杂一些。描述符的作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。
    根据描述符规则,基本数据类型(byte、char、double、float、int、long、short、boolean)以及代表无返回值的void类型都用一个大写字符表示,而对象类型则用字符L加对象的全限定名来表示。


    描述符标识字符含义.png
    • 对于数组类型,每一维度将使用一个前置的“[”字符来描述。
      如“java.lang.String[][]”类型的二维数组,将记录为“[[Ljava/lang/String”;
      一个整型数组“int[]”记录为“[I”。
    • 用描述符来描述方法时,按照先参数列表,后返回值的顺序描述,参数列表按照参数的严格顺序放在一组小括号“()”之内。
      如方法void inc()的描述符为“()V”;
      方法java.lang.String toString()的描述符为“()Ljava/lang/String”;
      方法int indexOf(char[] source,int sourceOffset,int sourceCount,char[] target,int targetOffset,int targetCount,int fromIndex"的描述符为“([CII[CIII)I”。
  • 字段表都包含的固定数据项目到descriptor_index为止就结束了。不过在descriptor_index之后跟随着一个属性表集合用于存储一些额外的信息,字段都可以在属性表中描述零至多项的额外信息。


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

  • 在Java语言中字段是无法重载的,两个字段的数据类型、修饰符不管是否相同,都必须使用不一样的名称,但对于字节码来讲,若两个字段的描述符不一致,那字段重名就是合法的。

六、方法表集合

  • Class文件存储格式中对方法的描述与对字段的描述几乎采用完全一致的方式,方法表的结构如同字段表一样。


    方法表结构.png

    字段访问标志.png
  • 方法里的Java代码,经过编译器编译成字节码指令后,存放在方法属性表集合中一个名为“Code”的属性里面,属性表作为Class文件格式中最具扩展性的一种数据项目。


    方法表结构实例.png
  • 与字段表集合想对应的,如果父类方法在子类中没有被重写(override),方法表集合中就不会出现来自父类的方法信息。同样的,有可能会出现由编译器自动添加的方法,最典型的便是类构造器“”方法和实例构造器“”方法。
  • Java语言中,重载(Overload)一个方法,除了要与原方法拥有相同的简单名称,还必须拥有与原方法不同的特征签名(一个方法中各个参数在常量池中的字段符号引用的集合),也就是返回值不会包含在特征签名中,因此Java语言里面无法仅靠返回值不同来对一个方法重载。
    但在Class文件格式中,特征签名的范围更大一些,只要描述符不是完全一致的两个方法也可共存。即如果两个方法有相同的名称和特征签名,但返回值不同,那么也可合法共存在同一个Class文件中。

七、属性表集合

  • 属性表集合(attriutes_info)在Class文件、字段表、方法表都可以携带自己的属性表集合,用以描述某些场景专有的信息。
  • 属性表集合的限制相比Class文件中其它信息项宽松,不再要求各个属性表具有严格顺序,并只要不与已有属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息,Java虚拟机运行时会忽略掉它不认识的属性。


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


    属性表结构.png

7.1 Code属性

Java程序方法体中的代码经过Javac编译器处理后,最终变成字节码指令存储在Code属性内。Code属性出现在方法表的属性集合中(并非所有方法表都存在此属性,接口或抽象类的方法中就不存在)。


Code属性表的结构.png
  • attribute_name_index与attribute_length
    一项指向CONSTANT_Utf8_info型常量的索引,常量值固定为“Code”,代表该属性的名称。
    attribute_length指示了属性值的长度,由于属性名称和长度一共6个字节,所以属性的长度固定为整个属性表长度减去6个字节。
  • max_stack
    代表操作数栈(Operand Stacks)深度的最大值。
    方法执行的任意时刻,操作数栈都不会超过这个深度。
    虚拟机运行时需根据这个值来分配栈帧(Stcak Frame)中的操作栈深度。
  • max_locals
    代表局部变量表所需的存储空间。
    单位为slot(虚拟机为局部变量分配内存所使用的最小单位)。
    对于byte、char、float、int、short、boolean和returnAddress等长度不超32位数据类型占1个slot;而long和double两种64位数据类型需2个slot。
    方法参数(含实例方法中的隐式“this”)、显示异常处理器的参数(Exception Handler Paramter,即catch块定义的异常)、方法体中定义的局部变量都需局部变量表来存放。
    局部变量中的slot可重用,当代码执行超出一个局部变量的作用域时,这个局部变量所占用的slot可被其它局部变量所使用,Javac编译器会根据变量的作用域来分配Slot给各个变量使用,然后计算出max_locals的大小。
  • code_length和code
    code_length(字节码长度)和code(用于存储字节码指令的一系列字节流)用来存储程序编译后生成的字节码指令。
    每个字节码指令就是一个u1类型的单字节,当虚拟机读到code中一个字节码时,可对应找出这个字节码代表什么指令,并可知道这条指令后面是否需要跟随参数,参数应当如何理解。u1数据类型的取值范围为0x00-0xFF,对应十进制0-256,即可代表256条指令。目前虚拟机规范已定义其中约200多条编码值对应的指令含义。
    • code_length
      虽然是u4类型的长度值,理论最大值可达2^32-1,但虚拟机规范限定方法不允许超过65535条字节码指令,即实际只使用了u2长度,超过了这个限制,javac编译器会拒绝编译。
    • code
      Code属性是Class文件中最重要的一个属性,如果把Java程序中的信息分为代码(Code,方法体里的Java代码)和元数据(Metadata,包括类、字段、方法定义及其他信息)两部分,那么Code属性用于描述代码,所有其他数据项目用于描述元数据。

在任何势力方法里面,都可以通过“this”关键字访问到此方法所属的对象。这个访问机制对Java程序的编写非常重要,实现是通过javac编译器编译的时候把对this关键字的访问转变为对一个普通方法参数的访问,然后在虚拟机调用实例方法时自动传入此参数。因此在实例方法的局部变量表中至少会存在一个指向当前对象实例的局部变量,局部变量表也会预留出第一个Slot位来存放对象实例的引用,方法参数值从1开始计算,这个处理只对实例有效。

  • 在字节码指令之后是这个方法显示异常处理表集合,异常对于Code属性来说并不是必须存在的。


    属性表(异常表)结构.png
    1. 如果当字节码在第start_pc行到第end_pc行之间(不含end_p行)出现了类型为catch_type或者其子类的异常(catch_type为指向一个CONSTANT_Class_info型常量的索引),则转到第handler_pc行继续处理。当catch_type的值为0时,代表任意异常情况都需要转向到handler_pc处进行处理。
    2. 异常表实际上是Java代码的一部分,编译器使用异常表而不是简单的跳转命令来实现Java异常及finally处理机制。
    3. Java代码的语义上来讲,异常3条执行路径分别为
      • 如果try语句块中出现属于Exeption或子类的异常,则转到catch语句块处理。
      • 如果try语句块中出现不属于Exception或其子类的异常,则转到finally语句块处理。
      • 如果catch语句块中出现任何异常,则转到finally语句块处理。

7.2 Exceptions属性

  • 这里的Exceptions属性是在方法中与Code属性平级的一项属性。
  • 作用是列举出方法中可能抛出的受查异常(Checked Exceptions),即方法描述时在throws关键字后列举的异常。


    属性表(Exceptions)结构.png

    number_of_exceptions项表示方法可能抛出number_of_exceptions种受查异常,每一种受查异常使用一个exception_index_table项表示,exception_index_table是一个指向常量池中CONSTANT_Utf8_info型常量的索引,代表该受查异常的类型。

7.3 LineNumberTable属性

  • 用于描述Java源码行号与字节码行号(字节码偏移量)之间的对应关系。
  • 并非必须属性,但默认生成在Class文件之中,可在javac中分别使用-g:none或-g:lines选项来取消或生成这项信息。如果不生成,对程序 运行产生的最主要影响就是抛出异常时,堆栈中将不会显示出错的行号,并在调试的时候,也无法按照源码行来来设置断点。


    LineNumberTable属性结构.png

    line_number_table是一个数量为line_number_table_length、类型为line_number_info的集合,line_number_info表包括了start_pc(字节码行号)和line_number(Java源码行号)两个u2类型的数据项。

7.4 LocalVariableTable属性

  • 用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系。
  • 并非运行时必须属性,但默认生成在Class文件中,可在javac中分别使用-g:none或-g:vars选项来取消或生成这项消息。如果没有生成这项属性,最大的影响就是当其他人引用这个方法时,所有的参数名称都将会丢失。


    LocalVariableTable属性结构.png

    local_variable_info项目代表了一个栈帧与源码中的局部变量的关联。


    local_variable_info项目结构.png
    • start_pc和length分别代表了局部变量的生命周期开始的字节码偏移量及其作用范围覆盖的长度,两者结合起来就是这个局部变量在字节码之中的作用域范围。
    • name_index(局部变量的名称)和descriptor_index(局部变量的描述符),都是指向常量池中的CONSTANT_Utf8_info型常量的索引。
    • index是这个局部变量在栈帧局部变量表中Slot的位置。当这个变量数据类型是64位类型时(double和long),它占用的Slot为index和index+1两个。

7.5 SourceFile属性

  • 用于记录生成这个Class文件的源码文件名称。
  • 可使用-g:none或-g:source来关闭或生成这项信息。在Java中,大多数类名和文件名是一致的,但有特殊情况(如内部类)。如果不生成这项属性,当抛出异常时,堆栈中将不会显示出错代码所属的文件名。
  • 这个属性是一个定长的属性。


    SourceFile属性结构.png

    sourcefile_index是指向常量池中CONSTANT_Utf8_info型常量的索引,常量值是源码文件的文件名。

7.6 ConstantValue属性

  • 用于通知虚拟机自动为静态变量赋值。
  • 只有被static关键字修饰的变量(类变量)才可使用这项属性。对于非static类型的变量(实例变量)的赋值是在实例构造器中中进行的;面对类变量,有两种方式可选择:在类构造器方法中或使用ConstantValue属性。Sun Javac编译器的选择是:如果同时使用final和static来修饰一个变量(常量),且这个变量的数据类型是基本类型或java.lang.String,就生成ConstantValue属性来进行初始化,如果这个变量没被final修饰,或并未基本类型及字符串,则将会在方法中进行初始化。


    ConstantValue属性结构.png

    ConstantValue是一个定长属性,它的attribute_length数据值必须固定为2.constantvalue_index数据项代表了常量池中一个字面量常量的引用,根据字段类型的不同,字面量可以是CONSTANT_Long_info、CONSTANT_Float_info、CONSTANT_Double_info、CONSTANT_Integer_info、CONSATNT_String_info常量中的一个。

7.7 InnerClasses属性

  • 用于记录内部类与宿主类之间的关联。


    InnerClasses属性结构.png

    number_of_classes代表需记录多少个内部类信息,每个内部类的信息都由一个inner_classes_info表进行描述。


    inner_classes_info表的结构.png
    • inner_class_info_index(内部类的符号引用)和outer_class_info_index(宿主类的符号引用)都是指向常量池中CONSTANT_Class_info型常量的索引。
    • inner_name_index是指向常量池中CONSTANT_Utf8_info型常量的索引,代表这个内部类的名称,如果是匿名内部类,则值为0。
    • inner_class_access_flags是内部类的访问标志,类似类的access_flags。


      inner_class_access_flags标志.png

7.8 Deprecated及Synthetic属性

  • 都属于标志类型的布尔属性,只存在有和没有的区别,没有属性值的概念。
  • Deprecated用于表示某个类、字段或方法已被程序作者定为不在推荐使用,通过@deprecated注解进行设置。
  • Synthetic代表此字段或方法并不是由Java源码直接产生的,而是由编译器自行添加的。


    Deprecated及Synthetic属性的结构.png

    attribyte_length的值必须为0x00000000,因为没有任何属性需要设置。

7.9 StackMapTable属性

  • JDK1.6之后新增,是一个复杂的边长属性,位于Code属性表中,会在虚拟机类加载的字节码验证阶段被新类型检查验证器(Type Checker)使用,目的在于替换以前比较消耗性能的基于数据流分析的类型推导验证器。

新的类型检查器在同样能保证Class文件合法性的前提下,省略了在运行期通过数据流分析去确认字节码的行为逻辑合法性的步骤,而是在编译阶段将一系列的验证类型(Verification Types)直接记录在Class文件之中,通过检查这些验证类型代替了类型推导过程,从而大幅度提升了字节码验证的性能。

  • StackMapTable中包含零至多个栈映射帧(Stack Map Frames),每个栈映射帧都显示或隐式的代表了一个字节码偏移量,用于表示该执行到该字节码时局部变量表和操作数栈的验证类型。类型检查验证器会通过检查目标方法的局部变量和操作数栈锁需的类型来确定一段字节码指令是否符合逻辑约束。


    StackMapTable属性的结构.png

    虚拟机规范中规定:版本大于等于50.0的Class文件中,若方法Code属性中没附带StackMapTable属性,就意味着带有隐式的StackMap属性,作用等同于number_of_entries值为0的StackMapTable属性。一个方法的Code属性最多只能由一个StackMapTable属性,否则抛出ClassFormatError异常。

7.10 Signature属性

  • JDK1.5新增,一个可选的定长属性,可出现于类、字段表和方法表结构的属性表中。JDK1.5之后,任何类、接口、初始化方法或成员的泛型签名如果包含了类型变量(Type Variables)或参数化类型(Parameterized Types),则Signature属性会为它记录泛型签名信息。
  • 专门用一个属性记录泛型类型,是因为Java语言的泛型采用的是擦除法实现的为泛型。在字节码(Code属性)中,泛型信息编译(类型变量、参数化类型)之后都通通被擦除掉。擦除的好处在于实现简单,非常容易实现Backport,运行期节省一些类型所占内存空间。坏处是运行期就无法像C#等有真泛型支持的语言那样。Signature就是为了弥补这个缺陷而增设,反射API能够获得泛型类型,最终的数据来源就是这个属性。


    Signature属性的结构.png

    signature_index项的值必须是一个对常量池的有效引用。常量池在该索引处的项必须是CONSTANT_Utr8_info结构,表示类签名、方法类型签名或字段类型签名。若当前是类文件的属性,则为类签名;如果是方法表的属性,则是方法类型签名;如果是字段表的属性,则是字段类型签名。

7.11 BootstrapMethods属性

  • JDK1.7新增,一个复杂的变长属性,位于类文件的属性表中。用于保存invokedynamic指令引用的引导方法限定符。
  • 虚拟机规范中规定,若某个类文件结构的常量池中曾经出现过CONSTANT_InvokeDynamic_info类型的常量,那么类文件的属性表中必须存在一个明确的BootstrapMethods属性,另外,即使CONSTANT_InvokeDynamic_info类型的常量在常量池中出现过多次,类文件的属性表中最多也只能有一个BootstrapMethods属性。


    BootstrapMethods属性的结构.png

    bootstrap_method属性的结构.png

    num_bootstrap_methods项的值给出了bootstrap_methods[]数组中引导方法限定符的数量。bootstrap_methods[]数组的每个成员包含了一个指向常量池CONSTANT_MethodHandle结构的索引值,它代表了一个引导方法,还包含了这个引导方法静态参数的序列(可能为空)。
    bootstap_methods[]数组中的每个成员必须包含以下三项内容

    1. bootstrap_method_ref
      值必须是一个对常量池的有效索引。常量池在该索引处的值必须是一个CONSTANT_MethodHandle_info结构。
    2. num_bootstrap_arguments
      值给出了bootstrap_arguments[]数组成员的数量。
    3. bootstrap_arguments[]
      数组的每个成员必须 是一个对常量池的有效索引。常量池在该索引处必须是下列结构之一:CONSTANT_String_info、CONSTANT_Class_info、CONSTANT_Float_info、CONSTANT_Double_info、CONSTANT_MethodHandle_info或CONSTANT_MethodType_info。

你可能感兴趣的:(类文件结构【Class类文件的结构】)