Java虚拟机规范 Java SE 8版 - class文件格式(二)

Java虚拟机规范 Java SE 8版 - class文件格式(二)

    • 4.5 字段
    • 4.6 方法
    • 4.7 属性
      • 4.7.1 自定义和命名新的属性
      • 4.7.2 ConstantValue 属性
      • 4.7.3 Code 属性
      • 4.7.4 StackMapTable 属性
      • 4.7.5 Exceptions 属性
      • 4.7.6 InnerClasses 属性
      • 4.7.7 EnclosingMethod 属性
      • 4.7.8 Synthetic 属性
      • 4.7.9 Signature 属性
        • 签名
      • 4.7.10 SourceFile 属性
      • 4.7.11 SourceDebugExtension 属性
      • 4.7.12 LineNumberTable 属性
      • 4.7.13 LocalVariableTable 属性
      • 4.7.14 LocalVariableTypeTable 属性
      • 4.7.15 Deprecated 属性
      • 4.7.16 RuntimeVisibleAnnotations 属性
      • 4.7.17 RuntimelnvisibleAnnotations 属性
      • 4.7.18 RuntimeVisibleParameterAnnotations 属性
      • 4.7.19 RuntimelnvisibleParameterAnnotations 属性
      • 4.7.20 RuntimeVisibleTypeAnnotations 属性
        • 4.7.20.1 target_info 联合体
        • 4.7.20.2 type_path 结构体
      • 4.7.21 RuntimelnvisibleTypeAnnotations 属性
      • 4.7.22 AnnotationDefault 属性
      • 4.7.23 BootstrapMethods 属性
      • 4.7.24 Methodparameters 属性

4.5 字段

每个字段(field)都由field_info结构所定义

在同一个class文件中,不会有两个字段同时具有相同的字段名和描述符(见4.3.2小节)。 field_info结构格式如下:

field_info {
	u2	access_flags;
	u2	name_index;
	u2	descriptor_index;
	u2	attributes_count;
	attribute_info attributes[attributes_count]}

field_info结构各项的说明如下:

  • access_flags
    access_flags 项的值是个由标志构成的掩码,用来表示字段的访问权限和基本属性。access_flags 中的每个标志,开启后的含义如表4-4所示。

表4-4 表示字段访问权限和属性的各个标志

标志名 说明
ACC_PUBLIC 0x0001 声明为public,可以从包外访问
ACC_PRIVATE 0x0002 声明为private,只能在定义该字段的类中访问
ACC_PROTECTED 0x0004 声明为protected,子类可以访问
ACC_STATIC 0x0008 声明为static
ACC_FINAL 0x0010 声明为final,对象构造好之后,就不能直接设置该字段了 (JLS § 17.5)
ACC_VOLATILE 0x0040 声明为volatile,被标识的字段无法缓存
ACC_TRANSIENT 0x0080 声明为transient,被标识的字段不会为持久化对象管理器所写入或读取
ACC_SYNTHETIC 0x1000 被表示的字段由编译器产生,而没有写源代码中
ACC_ENUM 0x4000 该字段声明为某个枚举类型(enum)的成员

class文件中的字段可以设置多个如表4-4所示的标志。不过有些标志是互斥的,一个字段最多只能设置 ACC_PRIVATE,ACC_PROTECTED 和 ACC_PUBLIC ( JLS § 8.3.1 ) 3 个标志中的一个,也不能同时设置标志 ACC_FINAL 和 ACC_VOLATILE (JLS §8.3.1.4)。接口中的所有字段都具有 ACC_PUBLIC、ACC_STATIC 和 ACC_FINAL 标志,也可以设置 ACC_SYNTHETIC 标志,但是不能含有表4-4中的其他标志(JLS §9.3)。

如果字段带有 ACC_SYNTHETIC 标志,则说明这个字段不是由源码产生的,而是由编译器自动产生的。
如果字段带有 ACC_ENUM 标志,这说明这个字段是一个枚举类型的成员。

表4-4 中没有出现的 access_flags 标志,是为了将来扩充而预留的,在生成的 class文件中应设置成0,Java虚拟机实现也应该忽略它们。

  • name_index
    name_index 项的值必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Utf8_info (见4.4.7小节)结构,此结构表示一个有效的非限定名,这个名称对应于本字段(见4.2.2小节)。
  • descriptor_index
    descriptor_index 项的值必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Utf8_info (见4.4.7小节)结构,此结构表示一个有效的字段的描述符(见4.3.2小节)。
  • attributes_count
    attributes_count 的项的值表示当前字段的附加属性(见4.7节)的数量
  • attributes[ ]
    属性表(attributes表)中的每个成员,其值必须是 attribute_info 结构(见4.7节)。 一个字段可以关联任意多个属性
    由本规范所定义,且可以出现在 field_info 结构 attributes 表里的各属性,列在表4-8中。
    field_info 结构 attributes 表里的各项预定义属性,其规则在4.7节中给出。 field_info 结构 attributes 表里的各项非预定义(non-predehned)属性,其规则在4.7.1小节中给出。

4.6 方法

所有方法(method),包括实例初始化方法以及类或接口初始化方法(见2.9节)在内, 都由 method_info 结构来定义

在一个class文件中,不会有两个方法同时具有相同的方法名和描述符(见4.3.3小节)。

method_info 结构格式如下:

method_info {
	u2	access_flags;
	u2	name_index;
	u2	descriptor_index;
	u2	attributes_count;
	attribute_info attributes[attributes_count];
}

method_info 结构各项的说明如下:

  • access_flags
    access_flags 项的值是由用于定义当前方法的访问权限和基本属性的各标志所构成的掩码。各标志开启之后的含义,如表4-5所示。

表4-5 表示方法访问权限及属性的各标志

标志名 说明
ACC_PUBLIC 0x0001 声明为public,可以从包外访问
ACC_PRIVATE 0x0002 声明为private,只能从定义该方法的类中访问
ACC_PROTECTED 0x0004 声明为protected,子类可以访问
ACC_STATIC 0x0008 声明为static
ACC_FINAL 0x0010 声明为final,不能被覆盖(见5.4.5小节)
ACC_SYNCHRONIZED 0x0020 声明为synchronized,对该方法的调用,将包装在同步锁(monitor)里
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 该方法是由编译器合成的,而不是由源代码编译出来的

class文件中的方法可以设置多个如表4-5所示的标志,但是有些标志是互斥的:一 个方法只能设置ACC_PR工VATE、ACC_PROTECTED 和 ACC_PUBLIC 这3个标志中的一个(JLS § 8.4.3 )。

接口方法可以设置表4-5里面除 ACC_PROTECTED、ACC_FINAL、ACC_SYNCHRONIZED 及ACC_NATIVE 之外的标志(JLS §9.4)。如果class文件的版本号小于52.0,那么接口中的每个方法必须设置 ACC_PUBLIC 及 ACC_ABSTRACT 标志;如果class文件的版本号大于或等于52.0,那么接口中的每个方法,必须设置 ACC_PUBLIC 或 ACC_PRIVATE 标志中的一个。

如果一个方法被设置 ACC_ABSTRACT 标志,则这个方法不能被设置 ACC_ FINAL、ACC_NATIVE、ACC_PRIVATE、ACC_STATIC、ACC_STRICT 或 ACC_ SYNCHRONIZED 标志(JLS §8.4.3.1、JLS §8.4.3.3、JLS §8.4.3.4)。

实例初始化方法(见2.9节)只能被 ACC_PRIVATE、ACC_PROTECTED 和 ACC_ PUBLIC 中的一个标识;还可以设置 ACC_STRICT、ACC_VARARGS 和 ACC_ SYNTHETIC 标志,但是不能再设置表4-5中的其他标志了。

类或接口初始化方法(见2.9节)由Java虚拟机隐式自动调用,它们的 access_flags 项的值除了 ACC_STRICT 标志外,其他标志都将被忽略。

ACC_BRIDGE 标志用于说明这个方法是由Java编译器生成的桥接方法㊀。

㊀ 桥接方法是JDK 1.5引入泛型后,为了使Java的范型方法生成的字节码和1.5版本前的字节码相兼容,由 
编译器自动生成的方法。一译者注

ACC_VARARGS 标志用于说明方法在源码层的参数列表是否变长。如果是变长的,则在编译时,必须把方法的ACC_VARARGS 标志置1,其余方法的 ACC_VARARGS 标志必须置0。

如果方法被 ACC_SYNTHETIC 标志标识,这说明这个方法是由编译器生成的并且不会在源码中出现,少量的例外情况将在4.7.8小节中提到。

表4-5 中没有出现的 access_flags 标志位,是给将来预留的。它们在生成的class文件中应设置成0,Java虚拟机实现应该忽略它们。

  • name_index
    name_index 项的值必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Utf8_info (见4.4.7小节)结构,它要么表示一个特殊方法的名字(<init>或见2.9节),要么表示一个方法的有效非限定名 (见4.2.2小节)。

  • descriptor_index
    descriptor_index 项的值必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Utf8_info(见447小节)结构,此结构表示一个有效的方法的描述符(见4.3.3小节)。

      本规范在未来的某个版本中可能会要求:当 access_flags 项的 ACC_VARARGS 标
      记被设置时,方法描述符中的最后一个参数描述符必须是数组类型。
    
  • attributes_count
    attributes_count 的项的值表示当前方法的附加属性(见4.7节)的数量

  • attributes [ ]
    属性表的每个成员的值必须是attribute(见4.7节)结构
    一个方法可以有任意个关联属性
    由本规范所定义,且可以出现在 method_info 结构 attributes 表(属性表)里的各属性,列在表4-6中。
    method_info 结构 attributes 表里的各项预定义属性,其规则在4.7节中给出。 method_info 结构 attributes 表里的各项非预定义(non-predefined)属性,其规则在4.7.1小节中给出。

4.7 属性

属性(attribute)在class文件格式中的ClassFile(见4.1节)结构、field_ info(见4.5节)结构、method_info(见4.6节)结构 和 Code_attribute(见4.7.3小节)结构都有使用

所有属性的通用格式如下:

attribute_info {
	u2 attribute_name_index;
	u4 attribute_length;
	u1 info[attribute_length];
} 

对于任意属性,attribute_name_index 必须是对当前class文件的常量池的有效16位无符号索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示当前属性的名字。attribute_length 项的值给出了跟随其后的信息字节的长度,这个长度不包括 attribute_name_index 和 attribute_length 项的6个字节

本规范预定义了23个属性。它们以3种排列形式印在3张表格中,为了查阅方便,现将这3张表格解说如下:

  • 表4-6 按照属性在本章的小节序号来排列。每个属性旁边还列出了该属性首次定义哪个版本的class文件格式之中,以及该格式所对应的Java SE平台版本。(对于版本比较旧的class文件格式来说,列出的是该格式的JDK发行版本号,而不是Java SE平台的版本号。)
  • 表4-7 按照首次定义该属性的class文件格式版本号来排序。
  • 表4-8 按照每个属性在class文件中应该出现的位置来排序。

本规范提到的这些属性,也就是在class文件结构 attributes 表(属性表)里出现的这些预定义属性(predefined attribute),其名称是保留的,属性表中的自定义属性不能再使用这些名称。

凡是出现在 attributes 表中的预定义属性,本节都会专门用一个小节来指出其用法。 若是没有另行说明,那就意味着该属性可以在 attributes 表中出现任意次。

这些预定义属性,可以按照用途分为下面三组:

  1. 对Java虚拟机正确解读class文件起关键作用的5个属性
  • ConstantValue
  • Code
  • StackMapTable
  • Exceptions
  • BootstrapMethods
    对于版本是V的class文件来说,如果Java虚拟机能够识别这个版本为V的class文件,而V又大于或等于首次定义某属性的class文件格式版本,同时这个属性还出现在它应该出现的位置上,那么,虚拟机必须能够识别并正确读取此属性。
  1. 对Java SE平台的类库正确解读class文件起关键作用的12个属性
  • InnerClasses
  • EnclosingMethod
  • Synthetic
  • Signature
  • RuntimeVisibleAnnotations
  • RuntimelnvisibleAnnotations
  • RuntimeVisibleParameterAnnotations
  • RuntimelnvisibleParameterAnnotations
  • RuntimeVisibleTypeAnnotations
  • RuntimelnvisibleTypeAnnotations
  • AnnotationDefault
  • MethodParameters
    对于版本是V的class文件来说,如果Java SE平台的类库实现(an implementation of the class libraries)能够识别这个版本为V的class文件,而V又大于或等于首次定义某属性的class文件格式版本,同时这个属性还出现在它应该出现的位置上,那么,类库实现必须能够识别并正确读取此属性。
  1. 对Java虚拟机或Java SE平台类库能够正确解读class文件虽然不起关键作用,但却可以作为实用工具来使用的6个属性
  • SourceFile
  • SourceDebugExtension
  • LineNumberTable
  • LocalVariableTable
  • LocalVariableTypeTable
  • Deprecated
    Java虚拟机实现或Java SE平台类库实现可以有选择地使用这些属性。实现可以使用这些属性所包含的信息,但若不使用这些属性,则必须默默地忽略(silently ignore)它们

表4-6 class文件中预定义的属性(按讲解该属性的章节排序)

属性名 章节 class文件 Java SE
Constantvalue 4.7.2小节 45.3 1.0.2
Code 4.7.3小节 45.3 1.0.2
StackMapTable 4.7.4小节 50.0 6
Exceptions 4.7.5小节 45.3 1.0.2
InnerClasses 4.7.6小节 45.3 1.1
EnclosingMethod 4.7.7小节 49.0 5.0
Synthetic 4.7.8小节 45.3 1.1
Signature 4.7.9小节 49.0 5.0
SourceFile 4.7.10小节 45.3 1.0.2
SourceDebugExtension 4.7.11小节 49.0 5.0
LineNumberTable 4.7.12小节 45.3 1.0.2
LocalVariableTable 4.7.13小节 45.3 1.0.2
LocalVariableTypeTable 4.7.14小节 49.0 5.0
Deprecated 4.7.15小节 45.3 1.1
RuntimeVisibleAnnotations 4.7.16小节 49.0 5.0
RuntimeInvisibleAnnotations 4.7.17小节 49.0 5.0
RuntimeVisibleParameterAnnotations 4.7.18小节 49.0 5.0
RuntimeInvisibleParameterAnnotations 4.7.19小节 49.0 5.0
RuntimeVisibleTypeAnnotations 4.7.20小节 52.0 8
RuntimelnvisibleTypeAnnotations 4.7.21小节 52.0 8
AnnotationDefault 4.7.22小节 49.0 5.0
BootstrapMethods 4.7.23小节 51.0 7
MethodParameters 4.7.24小节 52.0 8

表4-7 class文件中预定义的属性(按class文件版本排序)

属性名 class文件 Java SE 章节
Constantvalue 45.3 1.0.2 4.7.2小节
Code 45.3 1.0.2 4.7.3小节
Exceptions 45.3 1.0.2 4.7.5小节
SourceFile 45.3 1.0.2 4.7.10小节
LineNumberTable 45.3 1.0.2 4.7.12小节
LocalVariableTable 45.3 1.0.2 4.7.13小节
InnerClasses 45.3 1.1 4.7.6小节
Synthetic 45.3 1.1 4.7.8小节
Deprecated 45.3 1.1 4.7.15小节
EnclosingMethod 49.0 5.0 4.7.7小节
Signature 49.0 5.0 4.7.9小节
SourceDebugExtension 49.0 5.0 4.7.11小节
LocalVariableTypeTable 49.0 5.0 4.7.14小节
RuntimeVisibleAnnotations 49.0 5.0 4.7.16小节
RuntimeinvisibleAnnotations 49.0 5.0 4.7.17小节
RuntimeVisibleParameterAnnotations 49.0 5.0 4.7.18小节
RuntimeInvisibleParameterAnnotations 49.0 5.0 4.7.19小节
AnnotationDefault 49.0 5.0 4.7.22小节
StackMapTable 50.0 6 4.7.4小节
BootstrapMethods 51.0 7 4.7.23小节
RuntimeVisibleTypeAnnotations 52.0 8 4.7.20小节
RuntimelnvisibleTypeAnnotations 52.0 8 4.7.21小节
Methodparameters 52.0 8 4.7.24小节

表4-8 class文件中预定义的属性(按属性应该出现的位置排序)

属性名 位置 class文件
SourceFile ClassFile 45.3
InnerClasses ClassFile 45.3
EnclosingMethod ClassFile 49.0
SourceDebugExtension ClassFile 49.0
BootstrapMethods ClassFile 51.0
Constantvalue field_info 45.3
Code method_info 45.3
Exceptions method_info 45.3
RuntimeVisibleParameterAnnotations,RuntimeInvisibleParameterAnnotations method_info 49.0
AnnotationDefault method_info 49.0
MethodParameters method_info 52.0
Synthetic ClassFile,field_info,method_info 45.3
Deprecated ClassFile,field_info,method_info 45.3
Signature ClassFile,field_info,method_info 49.0
RuntimeVisibleAnnotations,RuntimeInvisibleAnnotations ClassFile,field_info,method_info 49.0
LineNumberTable Code 45.3
LocalVariableTable Code 45.3
LocalVariableTypeTable Code 49.0
StackMapTable Code 50.0
RuntimeVisibleTypeAnnotations,RuntimeInvisibleTypeAnnotations ClassFile,field_info,method_info,Code 52.0

4.7.1 自定义和命名新的属性

Java虚拟机规范允许编译器在class文件的class文件结构、field_info结构、 method_info结构以及Code属性的属性表中定义和发布新的属性Java虚拟机实现允许识别并使用这些属性表中出现的新属性。但是,所有未在class文件规范中定义的属性,不能影响class文件的语义。Java虚拟机实现必须忽略它不能识别的自定义属性

例如,编译器可以定义新的属性用于支持与特定发行者相关(vendor-specific)的调试, 而不影响其他Java虚拟机实现。因为Java虚拟机实现必须忽略它们不能识别的属性,所以与特定发行者相关的虚拟机实现所使用的class文件也可以被别的Java虚拟机实现使用, 即使这些class文件包含的附加调试信息不能被那些虚拟机实现所用。

Java虚拟机规范明确禁止Java虚拟机实现仅仅因为class文件包含新属性而抛出异常或以其他形式拒绝使用class文件。当然,如果class文件没有包含所需的属性,那么某些工具可能无法正确操作这个class文件。

当两个不同的属性使用了相同的属性名且长度也相同时,无论虚拟机识别其中哪一个,都会引起冲突。本规范定义之外的自定义属性,必须按照《Java语言规范(Java SE8版)》 (JLS § 6.1 )中所规定的包命名方式来命名。

本规范在未来的版本中可能会再增加一些预定义的属性。

4.7.2 ConstantValue 属性

Constantvalue属性是定长属性,位于field_info (见4.5节)结构的属性表中Constantvalue属性表示一个常量表达式(JLS § 15.28)的值,其用法如下:
如果该字段为静态字段(即field_info结构的access_flags项设置了 ACC_STATIC 标志),则说明这个field_info结构所表示的字段,将赋值为它的Constantvalue属性所表示的值,这个过程也是该字段所在类或接口初始化阶段(见5.5节)的一部分。这个过程发生在调用类或接口的类初始化方法(见2.9节)之前

  • 如果field_info结构表示的非静态字段包含了 Constantvalue属性,那么这个属性必须被虚拟机所忽略。
    在field_info结构的属性表中,最多只能有一个Constantvalue属性

    Constantvalue属性的格式如下:
ConstantValue_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u2 constantvalue_index;
}

ConstantValue_attribute结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示字符串 “ConstantValue ”
  • attribute_length
    ConstantValue_attribute 结构的 attribute_length 项的值固定为 2
  • constantvalue_index
    constantvalue_index 项的值必须是对常量池的一个有效索引。常量池表在该索引处的成员给出了该属性所表示的常量值。这个常量池项的类型必须适用于当前字段,适用关系见表4-9。

表4-9 ConstantValue属性的类型

字段类型 项类型
long CONSTANT_Long
float CONSTANT_Float
double CONSTANT_Double
int、short、char、byte、boolean CONSTANT_Integer
String CONSTANT_String

4.7.3 Code 属性

Code属性是变长属性,位于method_info(见4.6节)结构的属性表中。Code属性中包含某个方法、实例初始化方法、类或接口初始化方法(见2.9节)的Java虚拟机指令及相关辅助信息

如果方法声明为 native 或者 abstract 方法,那么 method_info 结构的属性绝不能有Code属性。在其他情况下,method_info 必须有且只能有一个Code属性

Code属性的格式如下:

Code_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u2 max_stack;
	u2 max_locals;
	u4 code_length;
	u1 code[code_length];
	u2 exception_table_length;
	{	 	u2 start_pc;
			u2 end_pc;
			u2 handler_pc;
			u2 catch_type;
	} exception_table [ exception_table_length ];
	u2 attributes_count;
	attribute_info attributes [ attributes_count ];
} 

Code_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示字符串 “Code”
  • attribute_length
    attribute_length 项的值给出了当前属性的长度,不包括初始的6个字节
  • max_stack
    max_stack 项的值给出了当前方法的操作数栈在方法执行的任何时间点的最大深度(见2.6.2小节)。
  • max_locals
    max_locals 项的值给出了分配在当前方法引用的局部变量表中的局部变量个数(见2.6.1小节),其中也包括调用此方法时用于传递参数的局部变量
    long和double类型的局部变量的最大索引是 max_locals - 2,其他类型的局部变量的最大索引是 max_locals - 1
  • code_length
    code_length 项的值给出了当前方法 code [ ] 数组的字节数
    code_length 的值必须大于0,即 code [ ] 数组不能为空
  • code [ ]
    code [ ] 数组给出了实现当前方法的Java虚拟机代码的实际字节内容
    当一台可按字节寻址的(byte-addressable)机器把code数组读入内存时,如果 code [ ] 数组的第一个字节是按4字节边界对齐,那么tableswitch和lookupswitch指令中所有的32位偏移量也都是按4字节长度对齐的(关于 code[ ] 数组边界对齐方式对字节码的影响,请参考相关指令的描述)。
    本规范对关于 code[ ] 数组内容的详细约束有很多,将在后面的4.9节中列出。
  • exception_table_length
    exception_table_length 项的值给出了 exception_table 表的成员个数
  • exception_table [ ]
    exception_table [ ] 数组的每个成员表示 code [ ] 数组中的一个异常处理器。exception_table [ ] 的异常处理器顺序是有意义的(不能随意更改,详细内容见2.10节)。
    exception_table [ ] 的每个成员包含如下4项:
    • start_pc 和 end_pc
      start_pc 和 end_pc 两项的值表明了异常处理器在 code [ ] 中的有效范围。 start_pc 的值必须是对当前code [ ] 中某一指令操作码的有效索引,end_ pc 的值要么是对当前 code [ ] 中某一指令操作码的有效索引,要么等于code_ length的值,即当前 code [ ] 的长度。start_pc 的值必须比 end_pc 小。
      当程序计数器在范围 [ start_pc,end_pc )内时,异常处理器就将生效。即设 x 为异常处理器的有效范围内的值,x 应满足:start_pc ≤ x < \leq x < x< end_pc。
      end_pc 本身并不处在异常处理器的有效范围内,这一点属于Java虚拟机历史上的一个设计缺陷:如果Java虚拟机中某方法的Code属性长度刚好是65 535个字节, 并且以一个单字节长度的指令结束,那么这个指令将不能被异常处理器所处理。不过编译器可以通过限制任何方法、实例初始化方法或静态初始化方法的 code[ ] 项最大长度为65 534来弥补这个Bug
    • handler_pc
      handler_pc 项的值表示一个异常处理器的起点。handler_pc 的值必须同时是对当前 code[ ] 和其中某一指令操作码的有效索引
    • catch_type
      如果 catch_type 项的值不为0,那么它必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Class_info(见4.4.1小节)结构, 用以表示当前异常处理器需要捕捉的异常类型。只有当抛出的异常是指定的类或其子类的实例时,才会调用异常处理器
      验证器(verifier)会检查这个类是不是Throwable或Throwable的子类(见4.9.2小节)。
      如果 catch_type 项的值为0,那么将会在所有异常抛出时都调用这个异常处理器。 这可以用于实现finally语句(见3.13节)。
  • attributes_count
    attributes_count 项的值给出了 Code 属性中 attributes [ ] 数组的成员个数
  • attributes [ ]
    属性表(attributes表)中的每个值都必须是 attribute_info 结构体(见4.7节)。

Code属性可以关联任意多个属性。

由本规范所定义且可以出现在Code属性attributes表里的各属性列在表4-8里。

Code属性attributes表里的各项预定义属性,其规则在4.7节中给出。

Code属性attributes表里的各项非预定义(non-predehned)属性,其规则在 4.7.1小节中给出。

4.7.4 StackMapTable 属性

StackMapTable属性是变长属性,位于Code (见4.7.3小节)属性的属性表中。这个属性用在虚拟机的类型检查验证阶段(见4.10.1小节)。

Code属性的属性表(attributes表)里面最多可以包含1个StackMapTable属性

在版本号大于或等于50.0的class文件中,如果方法的Code属性中没有附带 Stack-MapTable 属性,那就意味着它带有一个隐式的栈映射属性(implicit stack map attribute)。这个隐式的栈映射属性的作用等同于 number_of_entries 值为0的 StackMapTable 属性

StackMapTable 属性的格式如下:

StackMapTable_attribute {
	u2	attribute_name_index;
	u4	attribute_length;
	u2	number_of_entries;
	stack_map_frame entries [ number_of_entries ];
}

StackMapTable_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Utf8_info (见4.4.7小节)结构,用以表示字符串 "StackMapTable ”
  • attribute_length
    attribute_length 项的值表示当前属性的长度,不包括开头的6个字节
  • number_of_entries
    number_of_entries 项的值给出了 entries 表中的成员数量。entries表中每个成员都是一个 stack_map_frame 结构
  • entries [ ]
    entries 表中的每一项都表示本方法的一个栈映射帧(stack map frame)。entries 表中各栈映射帧之间的顺序是很重要的

栈映射帧(stack map frame) 显式或隐式地指定了某个字节码偏移量(bytecode offset),用来表示该帧所针对的字节码位置,并且指定了此偏移量处的局部变量和操作数栈项(operand stack entry)所需的核查类型(verification type)

entries表中的每个栈映射帧,其某些语义要依赖于它的前一个栈映射帧。方法的首个栈映射帧是隐式的,类型检查器(type checker,见4.10.1.6小节)会根据方法描述符来算出该帧。因此,stack_map_frame结构体中的 entries [0] 描述的是方法的下一个栈映射帧

㊀ 原文为second。如果把隐式的栈映射帧算作第0个,那么entries[0]描述的就是第1个栈映射帧;如 
果把隐式的栈映射帧算作第1个,那么entries[0]描述的就是第2个栈映射帧。——译者注
因为使用偏移量的增量,而没有直接使用实际的字节码偏移量,所以我们可以保证栈 
映射帧是按正确顺序存放的。此外,由于能通过公式 offset_delta + 1 来根据每 
个显式帧(不包括方法的首个帧,那个帧是隐式的)算出下一个显式帧的偏移量,因此 
可以避免重复。

核查类型(verification type)指出了一个或两个存储单元(location)的类型,而存储单元是指单个的局部变量或单个的操作数栈项。核查类型由可辨识的联合体(discriminated union) 来表示,此联合体名叫 verification_type_info,它包含长度为1个字节的标记(tag),用以指明当前使用联合体中的哪一项来表示核查类型,标记后面跟着0个或多个字节,用来给出与标记有关的更多信息

union verification_type_info {
	Top_variable_info;
	Integer_variable_info;
	Float_variable_info;
	Long_variable_info;
	Double_variable_info;
	Null_variable_info;
	UninitializedThis_variable_info;
	Object_variable_info;
	Uninitialized_variable_info;
}

如果核查类型针对的是局部变量表或操作数栈中的某一个存储单元,那么可以用 verification_type_info 联合体中的下面几项之一来表示

每个帧都显式或隐式地指明一个 offset_delta (偏移量的增量)值,用于计算每个帧在运行时的实际字节码偏移量。帧的字节码偏移量计算方法为:前一帧的字节码偏移量加上 offset_delta 的值再加1,如果前一个帧是方法的初始帧(initial frame),那么这时候字节码偏移量就是offset_delta。

在Code属性的 code[ ] 数组中,如果偏移量 i 的位置是某条指令的起点,同时这个 Code 属性包含StackMapTable属性,而它的 entries [ ] 数组中也有一个适用于偏移量 i 的 stack_map_frame 结构,那我们就说这条指令拥有与之相对应的栈映射帧。

  • Top_variable_info 项说明这个局部变量拥有核查类型 top
Top_variable_info {
	u1 tag = ITEM_Top;  /* 0 */ 
}
  • Integer_variable_info 项说明这个存储单元包含核查类型 int
Integer_variable_info { 
	u1 tag = ITEM_Integer; /* 1 */
} 
  • Float_variable_info 项说明存储单元包含核查类型 float
Float_variable_info {
	u1 tag = ITEM_Float; /* 2 */ 
}
  • Null_variable_info 项说明存储单元包含核查类型 null
Null_variable_info {
u1 tag = ITEM_Null; /* 5 */ 
} 
  • UninitializedThis_variable_info 项说明存储单元包含核查类型 uninitializedThis
UninitializedThis_variable_info { 
	u1 tag = ITEM_UninitializedThis; /* 6 */
}
  • Object_variable_info 项说明存储单元的核查类型是某个类。该类由常量池在 cpool_index 索引处的 CONSTANT_Class_Info (见4.4.1小节)结构表示
Object_variable_info { 
	u1 tag = ITEM_Object; /* 7 */ 
	u2 cpool_index;
}
  • Uninitialized_variable_info 项说明存储单元包含核查类型 uninitialized (offset)
Uninitialized_variable_info	{ 
	u1 tag = ITEM_Uninitialized /* 8 */ 
	u2 offset;
}

如果核查类型针对的是局部变量表或操作数栈中的某两个存储单元,那么可以用 verification_type_info 联合体中的下面几项之一来表示

  • Long_variable_info 项表示:这两个存储单元中的首个单元,拥有核查类型 long
Long_variable_info {
	u1 tag = ITEM_Long; /* 4 */ 
}
  • Double_variable_info 项表示:这两个存储单元中的首个单元,拥有核查类型 double
Double_variable_info {
	u1 tag = ITEM_Double; /* 3 */
}
  • Long_variable_info 项和 Double_variable_info 项也指明了这两个存储单元中的第二个单元,所拥有的核查类型
    • 如果两个存储单元中的首个单元是局部变量,那么:
      • 首个单元不能是索引值最大的局部变量。
      • 编号比首个单元大1的那个局部变量,拥有核查类型top。
    • 如果两个存储单元中的首个单元是操作数栈项,那么:
      • 首个单元不能位于栈顶。
      • 比首个单元更接近栈顶1个位置的那个存储单元,拥有核查类型top。

栈映射帧由名为 Stack_map_frame 的可辨识联合(discriminated union)来表示,该联合体包含长度为1字节的标记㊀**(tag),用以指明当前使用的是联合体中的哪一项,其后跟着0个或多个字节,用来给出与标记有关的更多信息**。

㊀ 为了强调该标记的作用,译文有时将其称为类型标记。——译者注
union stack_map_frame {
same_frame;
same_locals_l_stack_item_frame;
same_locals_l_stack_item_frame_extended;
chop_frame;
same_frame_extended;
append_frame;
full_frame;
}

类型标记用来表示栈映射帧的帧类型(frame type)

  • 帧类型same_frame的类型标记的取值范围是0~63。如果类型标记所确定的帧类型是same_frame类型,则表明当前帧拥有和前一个栈映射帧完全相同的 locals [ ] 表,并且对应的 stack [ ] 表的成员个数为0。当前帧的 offset_delta 值就使用 frame_type 项的值来表示㊁。
㊁ 此处描述的 stack、locals 是 StackMapTable 属性 entries 数组里某个 stack_map_frarme 中 
的项,它们与运行时栈帧中的操作数栈、局部变量表有映射关系,但并非同一种东西。原文中它们将描述 
为"stack” 和 "operand stack”、"locals” 和 "local variables",在本文中,指代属性项时 
使用 locals [ ] 表、stack[ ] 表来表示,而提到运行时栈帧时,则会明确翻译为操作数栈、局部变量表, 
也请读者根据上下文注意区分。——译者注
same_frame {
	u1 frame_type = SAME; /* 0-63 */
}
  • 帧类型 same_locals_l_stack_item_frame 的类型标记的取值范围是64 ~ 127。如果类型标记所确定的帧类型是same_locals_l_stack_item_frame 类型,则说明当前帧拥有和前一个栈映射帧完全相同的 locals[ ] 表,同时对应的stack[ ] 表的成员个数为1。当前帧的offset_delta值为frame_type-64。并且有一个verification_type_info项跟随在此帧类型之后,用于表示那一个栈项的成员。
same_locals_l_stack_item_frame {
	u1 frame_type = SAME_LOCALS_1_STACK_ITEM; /* 64-127 */ 
	verification_type_info stack [1];
} 
  • 范围在128 ~ 246的类型标记值是为未来使用而预留的。
  • 帧类型 same_locals_l_stack_item_frame_extended 由值为247的类型标记表示,它表明当前帧拥有和前一个栈映射帧完全相同的 locals [ ] 表,同时对应的 stack [ ] 表的成员个数为1。与帧类型same_locals_l_stack_item_frame不同,当前帧的offset_delta的值需要明确指定。有一个 stack [ ] 表的成员跟随在offset_delta项之后。
same_locals_l_stack_item_frame_extended  {
	u1 frame_type =-SAME_LOCALS_l_STACK_ITEM_EXTENDED; /* 247 */
	u2 offset_delta;
	verification_type_info stack[1];
}
  • 帧类型chop_frame的类型标记的取值范围是248~250。如果类型标记所确定的帧类型是chop_frame,则说明对应的操作数栈为空,并且拥有和前一个栈映射帧相同的 locals [ ] 表,但是该表缺少最后的 k 个 locals 项。k值由251-frame_ type确定。这种类型的帧会明确给出offset_delta值。
chop_frame {
	u1 frame_type = CHOP; /* 248-250 */
	u2 offset_delta;
} 
  • 帧类型same_frame_extended由值为251的类型标记表示。如果类型标记所确定的帧类型是same_frame_extended类型,则说明当前帧有拥有和前一个栈映射帧完全相同的 locals [ ] 表,同时对应的 stack[ ] 表的成员个数为0。与帧类型 same_frame 不同,这种类型的帧会明确给出offset_delta的值。
same_frame_extended {
	u1 frame_type = SAME_FRAME_EXTENDED; /* 251 */ 
	u2 offset_delta;
} 
  • 帧类型append_frame的类型标记的取值范围是252 ~ 254。如果类型标记所确定的帧类型为append_frame,则说明对应操作数栈为空,并且拥有和前一个栈映射帧相同的 locals [ ] 表,此外还附加了 k 个已定义的 locals 项。k值由 frame_type-251 确定。这种类型的帧会明确给出offset_delta值。
append_frame {
	u1 frame_type = APPEND; /* 252-254 */
	u2 offset_delta;
	verification_type_info locals[ frame_type - 251 ];
} 

locals [ ] 表中的第0项,表示首个附加局部变量的核查类型。如果 locals[M] 表示的是第N个局部变量,那么:

  • 若 locals [M] 是 Top_variable_info、Integer_variable_info、Float_variable_info、Null_variable_info、UninitializedThis_ variable_info、Object_variable_info 或 Uninitialized_variable_info 之一,则 locals [M+1] 表示第 N+1 个局部变量;
  • 若 locals [M] 是 Long_variable_info 或 Double_variable_info,则 locals [M+1] 表示第 N+2 个局部变量。

对于任意的索引 i,locals [ i ] 所表示的局部变量,其索引都不能大于此方法的局部变量表的最大索引值。

  • 帧类型full_frame由值为255的类型标志表示。这种类型的帧会明确给出 offset_delta 值。
full_frame {
	u1 frame_type = FULL_FRAME; /* 255 */
	u2 offset_delta;
	u2 number_of_locals;
	verification_type_info locals[number_of_locals];
	u2 number_of_stack_items;
	verification_type_info stack[number_of_stack_iterns];
} 

locals [ ] 表中的第0项表示0号局部变量的核查类型。如果 locals [M] 表示的是第N个局部变量,那么:

  • 若 locals[M] 是 Top_variable_info、Integer_variable_info、Float_variable_info、Null_variable_info、UninitializedThis_variable_info、Object_variable_info 或 Uninitialized_variable_info 之一,则 locals [M+1] 表示第 N+1 个局部变量;
  • 若 locals [M] 是 Long_variable_info、Double_variable_info,则 locals [M+1] 表示第 N+2 个局部变量。
    对于任意的索引 i,locals [ i ] 所表示的局部变量,其索引都不能大于此方法的局部变量表的最大索引值。

stack [ ] 表中的第0项表示操作数栈底部那一个元素的核查类型,stack [ ] 表中后续各项表示离栈顶较近的那些元素的核查类型。我们把操作数栈底部那一个元素称作0号(第0个)栈元素,并把操作数栈中后续各元素,分别称作1号(第1个)、2号(第 2个)栈元素,依此类推。如果 stack [M] 表示的是第N个栈元素,那么:

  • 若 stack [M] 是 Top_variable_info、Integer_variable_info、Float_variable_info、Null_variable_info、UninitializedThis_variable_info、Object_variable_info 或 Uninitialized_ variable_info 之一,则 stack [M+1] 表示第 N+1 个栈元素;
  • 若 stack [M] 是 Long_variable_info、Double_variable_info,则 stack [M+1] 表示第 N+2 个栈元素。
    对于任意的索引 i ,stack[ i ] 所表示的栈元素的索引都不能大于此方法操作数栈的最大深度。

4.7.5 Exceptions 属性

Exceptions属性是变长属性,位于method_info(见4.6节)结构的属性表中

**Exceptions属性指出了一个方法可能抛出的受检异常(checked exception)**㊀。 一个method_info结构的属性表中最多只能有一个Exceptions属性。 Exceptions属性的格式如下:

Exceptions_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u2 number_of_exceptions;
	u2 exception_index_table [ number_of_exceptions ];
} 
㊀ 受检异常这一术语的定义,请参阅《Java语言规范》(JavaSE8版)11.1.1小节。一一译者注 

Exceptions_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info (见447小节)结构,用以表示字符串 “Exceptions”
  • attribute_length
    attribute_length 项的值给出了当前属性的长度,不包括初始的6个字节
  • number_of_exceptions
    number_of_exceptions 项的值给出了 exception_index_table [ ] 中成员的数量
  • exception_index_table [ ]
    exception_index_table 数组的每个成员的值都必须是对常量池表的一个有效索引。常量池表在这些索引处的成员都必须是CONSTANT_Class_info(见4.4.1 小节)结构,表示这个方法声明要抛出的异常所属的类的类型

如果一个方法要抛出异常,必须至少满足下列三个条件之一

  • 要抛出的是RuntimeException或其子类的实例
  • 要抛出的是Error或其子类的实例
  • 要抛出的是在exception_index_table数组中声明的异常类或其子类的实例

这些要求并不在Java虚拟机中进行强制检查,它们只在编译时进行强制检查

4.7.6 InnerClasses 属性

InnerClasses属性是变长属性,位于ClassFile (见4.1节)结构的属性表中

本小节为了方便说明,特别定义一个表示类或接口的class格式为C。如果类或接口C的常量池中包含某个CONSTANT_Class_info成员,且这个成员所表示的类或接口不属于任何一个包,那么C的ClassFile结构的属性表中就必须含有且只能含有1个 InnerClasses 属性。

InnerClasses属性的格式如下:

InnerClasses_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u2 number_of_classes{ 
		u2 inner_class_info_index;
		u2 outer_class_info_index;
		u2 inner_name_index;
		u2 inner_class_access_flags;
	} classes[number_of_classes];
}

InnerClasses_attribute结构各项的说明如下:

  • attribute_name_index
    attribute_name_index项的值必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Utf8_info (见4.4.7小节)结构,用以表示字符串 “InnerClasses”
  • attribute_length
    attribute_length项的值给出了当前属性的长度,不包括初始的6个字节
  • number_of_classes
    number_of_classes项的值表示classes [ ] 数组的成员数量
  • classes [ ]
    常量池表中表示类或接口C,同时又不是某个包成员的每个CONSTANT_Class_ info项,都必须对应于classes数组里的一项。

如果类或接口的成员中又包含某些类或接口,那么它的常量池表(以及对应的InnerClasses属性)必须包含这些成员,即使那些类或接口没有被这个class使用过(JLS. § 13.1 )。

此外,常量池表中的每个嵌套类(nested class)㊀和嵌套接口 (nested interface)都必须引用其外围类(enclosing class),因此,这些嵌套类和嵌套接口的InnerClasses属性里面,既有其外围类的信息,也有其本身的嵌套类及嵌套接口的信息。

㊀ 在不致混淆的情况下,嵌套类和内部类这两个词可以互换。——译者注

class [ ] 数组中每个成员包含以下4项:

  • inner_class_info_index
    inner_class_info_index 项的值必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Class_info(JAL 4.4.1小节)结构,用以表示类或接口C。class数组的另外3项用于描述C的信息。
  • outer_class_info_index
    如果C不是类或接口的成员(也就是C为顶层类或接口(JLS §7.6)、局部类 (JLS § 14.3 )或匿名类(JLS § 15.9.5 )),那么 outer_class_info_index 项的值为0。
    否则这个项的值必须是对常量池表的一个有效索引,常量池表在该索引处的成员必须是CONSTANT_Class_info(见4.4.1小节)结构,代表一个类或接口,C为这个类或接口的成员。
  • inner_name_index
    如果C是匿名类(JLS § 15.9.5 ),inner_name_index 项的值则必须为0。
    否则这个项的值必须是对常量池表的一个有效索引,常量池表在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示由与C的class文件相对应的源文件所定义的C的原始简单名称(original simple name)。
  • inner_class_access_flags
    inner_class_access_flags 项的值是一个标志掩码,用于定义由与class文件对应的源文件所声明的C的访问权和基本属性。当编译器无法访问源文件时可用来恢复C的原始信息。inner_class_access_flags 项中的各标志,见表4-10。

表4-10 嵌套类的访问权标志及属性标志

标志 含义
ACC_PUBLIC 0x0001 该嵌套类型在源文件中标注为public,或默认就是public
ACC_PRIVATE 0x0002 该类型在源文件中标注为private
ACC_PROTECTED 0x0004 在源文件中标注为protected
ACC_STATIC 0x0008 在源文件中标注为static,或默认就是static
ACC_FINAL 0x0010 在源文件中标注为final
ACC_INTERFACE 0x0200 是源文件中定义的interface(接口)
ACC_ABSTRACT 0x0400 在源文件中标注为abstract,或默认就是abstract
ACC_SYNTHETIC 0x1000 声明为synthetic(合成),意味着源文件中没有这个类型
ACC_ANNOTATION 0x2000 声明为annotation(注解)类型
ACC_ENUM 0x4000 声明为enum(枚举)类型

所有表4-10中没有定义的 inner_class_access_flags 项,都是为未来使用而预留的。这些二进制位在生成的class文件中应设置为0,Java虚拟机实现应忽略它们。

如果class文件的版本号为51.0或更高,且属性表中又有InnerClasses属性,那么若InnerClasses属性的 classes [ ] 中某个 inner_name_index 项的值为0,则它对应的 outer_class_info_index 项的值也必须为0。

Oracle的Java虚拟机实现不会检查InnerClasses属性和该属性所引用的类或接口的class文件是否一致性。

4.7.7 EnclosingMethod 属性

EnclosingMethod属性是可选的定长属性,位于ClassFile (见4.1节)结构的属性表中。当且仅当class为局部类或者匿名类(JLS §14.3,JLS §15.9.5)时,才能具有 EnclosingMethod 属性

ClassFile结构的属性表中,最多只能有一个EnclosingMethod属性

EnclosingMethod属性格式如下:

EnclosingMethod_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u2 class_index;
	u2 method_index;
}

EnclosingMethod_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Utf8_info (见447小节)结构,用以表示字符串 "EnclosingMethod”
  • attribute_length
    attribute_length 项的值固定为4
  • class_index
    class_index 项的值必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Class_info(见4.4.1小节)结构,用以表示包含当前类声明的最内层类
  • method_index
    如果当前类不是直接包含(enclose)在某个方法或构造器中,那么method_index项的值必须为0

特别需要说明的是,如果当前类在源代码中直接处于实例初始化器(instance initializer)、 静态初始化器(static initializer)、实例变量初始化器(instance variable initializer) 或类变量初始化器(class variable initializer)之中,那么method_index 必须为0。(前两种情况涉及局部类和匿名类,后两种情况只涉及声明在字段赋值(field assignment)语句右侧的匿名类。)

否则 method_index 项的值必须是对常量池表的一个有效索引,常量池表在该索引处的项必须是CONSTANT_NameAndType_info(见4.4.6小节)结构,表示由 class_index 属性引用的类的对应方法的方法名和方法类型。

Java编译器有责任保证:通过method_index所确定的方法,在语法上最接近那个包含 EnclosingMethod 属性的类。

4.7.8 Synthetic 属性

Synthetic 属性是定长属性,位于ClassFile(见4.1节)、field_info(见4.5节)或 method_info(见4.6节)的属性表中如果一个类成员没有在源文件中出现,则必须标记带有Synthetic属性,或者设置ACC_SYNTHETIC 标志唯一的例外是某些与人工实现无关的、由编译器自动产生的方法,也就是指Java语言默认的实例初始化方法(无参数的默认构造器,见2.9节)、类初始化方法(见2.9节),以及Enum.values和Enum.valueOf 方法

Synthetic属性是在JDK 1.1中为了支持内部类或接口而引入的

Synthetic属性的格式如下:

Synthetic_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
}

Synthetic_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池表的一个有效索引,常量池表在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示字符串 “Synthetic”
  • attribute_length
    attribute_length项的值固定为0

4.7.9 Signature 属性

Signature属性是可选的定长属性,位于ClassFile(见4.1节)、field_info(见4.5节)或 method_info(见4.6节)结构的属性表中。在Java语言中,任何类、接口、构造器方法或字段的声明如果包含了类型变量(type variable)或参数化类型(parameterized type),则Signature属性会为它记录泛型签名信息。这些类型的详情可参阅《Java语言规范》(Java SE 8 版)。

Signature属性的格式如下:

Signature_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u2 signature_index;
}

Signature_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info (见4.4.7小节)结构,用以表示字符串 “Signature”
  • attribute_length
    Signature_attribute 结构的 attribute_length 项的值必须为2
  • signature_index
    signature_index 项的值必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是C0NSTANT_Utf8_info (见4.4.7小节)结构,用以表示类签名、方法类型签名或字段类型签名:如果当前的Signature属性是ClassFile结构的属性,则这个结构表示类签名;如果当前的Signature属性是method_info结构的属性,则这个结构表示方法类型签名;如果当前的Signature属性是field_ info结构的属性,则这个结构表示字段类型签名

Oracle的Java虚拟机实现在加载类或链接类的过程中并不检查Signature属性。Java SE平台的某些类库能够查询类、接口、构造器、方法及字段的泛型签名,而Signature属性的正确性就是由这些类库来检查的。比方说,Class类的 getGenericSuperclass 方法及 java.lang.reflect.Executable 类的 toGenericString 方法,就可以查询泛型签名。

签名

签名(Signature)用来编码以Java语言所写的声明,这些声明使用了Java虚拟机类型系统之外的类型。在只能访问class文件的情况下,签名有助于实现反射、调试及编译

如果类、接口、构造器、方法或字段的声明使用了类型变量或参数化类型,那么Java编译器就必须为此生成签名。具体来说,Java编译器在这些情况下必须生成签名

  • 当类声明或接口声明⑴具备泛型形式,或者⑵其超类或超接口是参数化类型,又或者前两条兼备时,必须生成类签名。
  • 当方法声明或构造器声明⑴具备泛型形式,或者⑵其形式参数类型或返回类型是类型变量或参数化类型,⑶或者在throws子句中使用了类型变量,又或者前述三条具备两条或三者兼备时,必须生成方法签名。
    如果方法声明或构造器声明的throws子句不涉及类型变量,那么编译器在生成方法签名时,可以将该声明视为不含throws子句的声明。
  • 当字段声明、形式参数声明或局部变量声明中的类型使用了类型参数或参数化类型时,必须为该声明生成字段签名。
    本规范采用4.3.1小节中的语法符号来指定签名。此外,还用到了下面一些记法:
    • 出现在规则右侧的 [x] 写法,表示 x 可以出现0次或1次。也就是说,x是可选符号 (optional symbol)。带有可选符号的规则,实际上是定义了两种格式:一种不包含可选符号,另一种包含可选符号。
    • 如果规则的右侧特别长,那么可以写到下一行去,此时,那一行会有明确的缩进。

带有终止符号以 Identifier 的语法规则用来表示Java编译器为类型、字段、方法、形式参数、局部变量或类型变量所生成的名称。这种名称里不能包含 . ; [ /< > :这七个ASCII字符中的任意一个字符(也就是说,既不能使用方法名(参见4.2.2小节)所禁止的字符,又不能使用冒号),但是可以包含不能出现在Java语言标识符中的字符(参见JLS §3.8)。

签名依赖于由非终止符号(nonterminal)所构成的体系,这些非终止符号,称为类型签名 (type signature)

  • Java类型签名(Java type signature)表示引用类型或Java语言中的原始类型
JavaTypeSignature:
	ReferenceTypeSignature
	BaseType

为了便于查阅,本规范把4.3.2小节对于BaseType的定义列在下面:

BaseType: one of
	B C D F I J S Z
  • 引用类型签名(reference type signature)表示Java语言的引用类型,也就是类或接口类型、类型变量,或者数组类型
    类的类型签名(class type signature)表示(可能已经参数化了的)类或接口类型。类的类型签名必须遵循固定的格式,也就是说,在把类型参数擦除掉,并把每个.字符换成$字符之后,它必须能够准确地与该类的二进制名称对应起来
    类型变量签名(type variable signature)用来表示类型变量
    数组类型签名(array type signature)用来表示数组类型的一个维度
ReferenceTypeSignature:
	ClassTypeSignature
	TypeVariableSignature
	ArrayTypeSignature

ClassTypeSignature:
	L [PackageSpecifier]
		SimpleClassTypeSignature {ClassTypeSignatureSuffix};

PackageSpecifier:
	Identifier / {PackageSpecifier}
	
SimpleClassTypeSignature:
	Identifier [TypeArguments]
	
TypeArguments:
	< TypeArgument {TypeArgument} >
	
TypeArgument:
	[Wildcardindicator] ReferenceTypeSignature
	*
	
Wildcardindicator:
	+
	-
	
ClassTypeSignatureSuffix:
	.SimpleClassTypeSignature
	
TypeVariableSignature:
	T Identifier;
	
ArrayTypeSignature:
	[JavaTypeSignature

类签名(class signature)用来编码类声明的类型信息(这个类可能是泛型类)。它描述了该类的类型参数,如果该类有直接超类或直接超接口(超类或超接口可能是参数化了的类或接口),那么类签名还会列出它们。类型参数通过其名称来描述,名称后面跟有任意的类限制(class bound)或接口限制(interface bound)

ClassSignature:
	[TypeParameters] SuperclassSignature {SuperinterfaceSignature}
	
TypeParameters:
	< TypeParameter {TypeParameter} >
	
TypeParameter:
	Identifier ClassBound {InterfaceBound}
	
ClassBound:
	:[ReferenceTypeSignature]
	
InterfaceBound: 
	:ReferenceTypeSignature
	
SuperclassSignature:
	ClassTypeSignature
	
SuperinterfaceSignature:
	ClassTypeSignature

方法签名(method signature)用来编码方法声明的类型信息(此方法可能是泛型方法)。 它描述了方法可能带有的类型参数、(可能已经参数化了的)形式参数类型、(可能已经参数化了的)返回类型,以及声明在方法throws子句中的异常类型

MethodSignature:
	[TypeParameters] ({JavaTypeSignature}) Result {ThrowsSignature}
	
Result:
	JavaTypeSignature
	VoidDescriptor
	
ThrowsSignature:
	^ ClassTypeSignature
	^ TypeVariableSignature

为了便于查阅,本规范把4.3.3小节对于VoidDescriptor的定义列在下面:

VoidDescriptor:
	V

由于各种编译器在生成方法签名时所用的机理不同,因此某方法的方法签名,未必会与该方法的方法描述符(参见4.3.3小节)完全匹配。尤其是方法签名中形式参数类型的个数,可能比方法描述符中参数描述符的个数要少

字段签名(field signature)用来编码字段声明、形式参数声明或局部变量声明中(可能已经参数化了)的类型

FieldSignature:
	ReferenceTypeSignature

4.7.10 SourceFile 属性

SourceFile属性是可选的定长属性,位于ClassFile(见4.1节)结构的属性表中

一个ClassFile结构的属性表中最多只能包含一个SourceFile属性

SourceFile属性的格式如下:

SourceFile_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u2 sourcefile_index;
}

SourceFile_attribute结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示字符串 “SourceFile”
  • attribute_length
    SourceFile_attribute 结构的 attribute_length 项的值必须为2
  • sourcefile_index
    sourcefile_index 项的值必须是对常量池表的一个有效索引。常量池表在该索引处的成员必须是CONSTANT_Utf8_info (见4.4.7小节)结构,用来表示一个字符串。 sourcefile_index 项引用的字符串表示被编译的class文件的源文件的名字。 不要把它理解成源文件所在目录的目录名或源文件的绝对路径名。这些与平台相关的附加信息,必须由运行时解释器(runtime interpreter)或开发工具在实际使用文件名时提供。

4.7.11 SourceDebugExtension 属性

SourceDebugExtension属性是可选属性,位于ClassFile(见4.1节)结构的属性表中。

一个ClassFile结构的属性表中最多只能包含一个SourceDebugExtension属性。

SourceDebugExtension属性的格式如下:

SourceDebugExtension_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u1 debug_extension[attribute_length];
} 

SourceDebugExtension_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示字符串 “SourceDebugExtension”
  • attribute_length
    attribute_length项的值给出了当前属性的长度,不包括初始的6个字节
  • debug_extension [ ]
    debug_extension [ ] 用于保存扩展调试信息,扩展调试信息对于Java虚拟机来说没有实际的语义这个信息用改进版的UTF-8编码的字符串(见4.4.7小节)表示, 这个字符串不包含byte值为0的终止符

需要注意的是,debug_extension [ ] 数组表示的字符串可以比String类的实例所能表示的字符串更长

4.7.12 LineNumberTable 属性

LineNumberTable属性是可选的变长属性,位于Code(见4.7.3小节)结构的属性表中。它被调试器用于确定源文件中由给定的行号所表示的内容,对应于Java虚拟机 code [ ] 数组中的哪一部分

在Code属性的属性表中,LineNumberTable属性可以按照任意顺序出现

在Code属性attributes表中,可以有不止一个LineNumberTable属性对应于源文件中的同一行。也就是说,多个LineNumberTable属性可以合起来表示源文件中的某行代码,属性与源文件的代码行之间不必有一一对应的关系

LineNumberTable属性的格式如下:

LineNumberTable_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u2 line_number_table_length;
	{ 	u2 start_pc;
		u2 line_number;
	} line_number_table [ line_number_table_length ];
}

LineNumberTable_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用来表示字符串 "LineNumberTable”
  • attribute_length
    attribute_length项的值给出了当前属性的长度,不包括初始的6个字节
  • line_number_table_length
    line_number_table_length 项的值给出了 line_number_table [ ] 数组的成员个数
  • line_number_table [ ]
    line_number_table [ ] 数组的每个成员都表明源文件中的行号会在code数组中的哪一条指令处发生变化
    line_number_table 的每个成员都具有如下两项:
    • start_pc
      start_pc 项的值必须是 code [ ] 数组的一个索引,code [ ] 在该索引处的指令码,表示源文件中新的行的起点。
      start_pc 项的值必须小于当前 LineNumberTable 属性所在的 Code 属性的 code_length 项的值。
    • line_number
      line_number 项的值必须与源文件中对应的行号相匹配。

4.7.13 LocalVariableTable 属性

LocalVariableTable 属性是可选变长属性,位于Code (见4.7.3小节)属性的属性表中。调试器在执行方法的过程中可以用它来确定某个局部变量的值。

在Code属性的属性表中,多个LocalVariableTable属性可以按照任意顺序出现

Code属性attributes表中的每个局部变量,最多只能有一个LocalVariableTable 属性

LocalVariableTable属性的格式如下:

LocalVariableTable_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u2 local_variable_table_length;
	{	u2 start_pc;
		u2 length;
		u2 name_index;
		u2 descriptor_index;
		u2 index;
	} local_variable_table[local_variable_table_length];
}

LocalVariableTable_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info (见4.4.7小节)结构,用以表示字符串 "LocalVariableTable”
  • attribute_length
    attribute_length项的值给出了当前属性的长度,不包括初始的6个字节
  • local_variable_table_length
    local_variable_table_length 项的值给出了 local_variable_table [ ] 数组的成员数量
  • local_variable_table [ ]
    local_variable_table数组中的每一项,都以偏移量的形式给出了code数组中的某个范围,当局部变量处在这个范围内的时候,它是有值的。此项还会给出局部变量在当前帧的局部变量表(local variable array)中的索引。local_variable_ table [ ] 的每个成员都有如下5个项:
    • start_pc 和 length
      当给定的局部变量处在code数组的[start_pc,start_pc + length)范围内,也就是处在由偏移量大于等于start_pc且小于 start_pc + length 的字节码所构成的范围内时,该局部变量必定具备某个值㊀。
      start_pc的值必须是对当前Code属性的 code [ ] 的一个有效索引,code [ ] 在这个索引处必须是一条指令的操作码。
      start_pc + length 要么是当前Code属性的 code [ ] 数组的有效索引,且 code [ ] 在该索引处必须是一条指令的操作码,要么是刚超过 code [ ] 数组末尾的首个索引值。
    • name_index
      name_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info (见4.4.7小节)结构,用来表示一个有效的非限定名,以指代这个局部变量(见4.2.2小节)。
    • descriptor_index
      descriptor_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info (见4.4.7小节)结构,此结构是个用来表示源程序中局部变量类型的字段描述符(见4.3.2小节)。
    • index
      index 为此局部变量在当前栈帧的局部变量表中的索引。
      如果在index索引处的局部变量是long或double类型,则占用 index 和 index+1 两个位置。
㊀ 这种说法可以理解为:当程序执行到code数组的[start_pc, start_pc + length)范围内时, 该局部变量是有效的。——译者注

4.7.14 LocalVariableTypeTable 属性

LocalVariableTypeTable属性是可选的变长属性,位于Code(见4.7.3小节)的属性表中。调试器在执行方法的过程中,可以用它来确定某个局部变量的值

在Code属性的属性表中,多个LocalVariableTable属性可以按照任意顺序出现。Code属性attributes表中的每个局部变量最多只能有一个LocalVariableTable属性

LocalVariableTypeTable 属性和 LocalVariableTable 属性(见4.7.13小节) 并不相同,LocalVariableTypeTable提供的是签名信息而不是描述符信息。这仅仅对使用类型变量或参数化类型来做其类型的变量有意义。这种变量会同时出现在 LocalVariableTable 属性和 LocalVariableTypeTable 属性中,其他的变量仅出现在 LocalVariableTable 表中

LocalVariableTypeTable 属性的格式如下:

LocalVariableTypeTable_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u2 local_variable_type_table_length;
	{	u2 start_pc;
		u2 length;
		u2 name_index;
		u2 signature_index;
		u2 index;
	} local_variable_type_table [local_variable_type_table_length];
} 

LocalVariableTypeTable_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示字符串 “LocalVariableTypeTable”
  • attribute_length
    attribute_length项的值给出了当前属性的长度,不包括初始的6个字节
  • local_variable_type_table_length
    local_variable_type_table_length 项的值给出了 local_variable_type_ table [ ] 数组的成员数量
  • local_variable_type_table [ ]
    local_variable_type_table 数组中的每一项都以偏移量的形式给出了 code 数组中的某个范围,当局部变量处在这个范围内的时候,它是有值的。此项还会给出局部变量在当前帧的局部变量表(local variable array)中的索引。local_variable_type_table [ ] 的每个成员都有如下5个项:
    • start_pc 和 length
      当给定的局部变量处在code数组的 [start_pc,start_pc + length)范围内,也就是处在由偏移量大于等于 start_pc 且小于 start_pc + length 的字节码所构成的范围内时,该局部变量必定具备某个值。
      start_pc 的值必须是对当前Code属性的 code [ ] 的一个有效索引,code [ ] 在这个索引处必须是一条指令的操作码。
      start_pc + length 要么是当前Code属性的 code [ ] 数组的有效索引,且 code [ ] 在该索引处必须是一条指令的操作码,要么是刚超过 code [ ] 数组末尾的首个索引值。
    • name_index
      name_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info (见4.4.7小节)结构,用来表示一个有效的非限定名,以指代这个局部变量(见4.2.2小节)。
    • signature_index
      signature_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info (见4.4.7小节)结构,此结构是个用来表示源程序中局部变量类型的字段签名(见4.7.9.1子小节)。
    • index
      index 为此局部变量在当前栈帧的局部变量表中的索引。
      如果在index索引处的局部变量是long或double类型,则占用 index 和 index+1 两个位置。

4.7.15 Deprecated 属性

Deprecated属性是可选定长属性,位于ClassFile(见4.1节)、field_info(见 4.5节)或 method_info(见4.6节)结构的属性表中。类、接口、方法或字段都可以带有Deprecated属性。如果类、接口、方法或字段标记了此属性,则说明它将会在后续某个版本中被取代

在运行时解释器或工具(比如编译器)读取class文件格式时,可以用Deprecated属性来告诉使用者避免使用这些类、接口、方法或字段,选择其他更好的方式。Deprecated属性的出现不会修改类或接口的语义。

Deprecated属性的格式如下:

Deprecated_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
} 

Deprecated_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示字符串 "Deprecated”
  • attribute_length
    attribute_length 项的值固定为0

4.7.16 RuntimeVisibleAnnotations 属性

RuntimeVisibleAnnotations 属性是变长属性,位于ClassFile(见4.1节)、field_info(见4.5节)或 method_info(见4.6节)结构的属性表中。RuntimeVisibleAnnotations 属性记录了添加在类声明、字段声明或方法声明上面㊀,且于运行时可见的注解。Java虚拟机必须令这些注解可供取用,以便使某些合适的反射API能够把它们返回给调用者。

㊀ 这里的上面只是一种泛称。有些注解可以添加在待注元素的左侧。具体位置请参阅《Java语言规范》中的 
相关内容。——译者注  

ClassFile、field_info 或 method_info 结构的属性表中,最多只能有一个RuntimeVisibleAnnotations属性

RuntimeVisibleAnnotations 属性的格式如下:

RuntimeVisibleAnnotations_attribute {
	u2	attribute_name_index;
	u4	attribute_length;
	u2	num_annotations;
	annotation annotations[num_annotations];
}	

RuntimeVisibleAnnotations_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示字符串 “RuntimeVisibleAnnotations”
  • attribute_length
    attribute_length 项的值给出了当前属性的长度,不包括初始的6个字节。
    attribute_length 项的值由当前结构的运行时可见注解的数量和值决定。
  • num_annotations
    num_annotations 项的值给出了当前结构所表示的运行时可见注解的数量
  • annotations[ ]
    annotations 数组的每个成员,都表示一条添加在声明上面的运行时可见注解

annotation结构的格式如下:

annotation {
	u2 type_index;
	u2 num_element_value_pairs;
	{ 	u2 element_name_index;
		element_value value;
	} element_value_pairs [num_e1ement_va1ue_pairs];
}

annotation 结构各项的说明如下:

  • type_index
    type_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用来表示一个字段描述符,这个字段描述符表示一个注解类型,它和当前annotation结构所表示的注解一致。
  • num_element_value_pairs
    num_element_value_pairs 项的值给出了当前annotation结构所表示的注解中的键值对个数。
  • element_value_pairs [ ]
    element_value_pairs [ ] 数组每个成员的值都对应于当前annotation结构所表示的注解中的一个键值对。element_value_pairs 数组的每个成员都包含如下两个项:
    • element_name_index
      element_name_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是 CONSTANT_Utf8_info (见4.4.7小节)结构,此结构用来指代 element_value_pairs 数组成员所表示的键值对中那个键的名字。换句话说,这个CONSTANT_Utf8_info结构用来指代由type_index所表示的那个注解类型中的一个元素(element)㊀名称。
    • value
      value 项的值给出了由 element_value_pairs 成员所表示的键值对中的那个 element_value 值。
㊀ 注解中的元素也称为配置参数或属性(attribute), 它的名称对应于键值对(element value pair)里的键 
(element_name_index);它的值对应于键值对里的值(value)。————译者注

element_value 结构
element_value 结构是一个可辨识联合体(discriminated union)㊁用于表示 “元素-值” 的键值对中的值

㊁ “discriminated union” 是一种数据结构,用于表示若干种具有独立特征的同类项集合。————译者注 

element_value 结构的格式如下:

element_value {
	u1 tag;
	union {
		u2 const_value_index;
		{	u2 type_name_index;
			u2 const_name_index;
		} enum_const_value;
		u2 class_info_index;
		annotation annotation_value;
		{ 	u2 num_values;
			element_value values [num_values];
		} array_value;
	} value;
}

tag 项使用一个ASCII字符来表示键值对中的值是什么类型。这个tag决定了键值对中值的格式与value联合体里的哪一项相符。表4-11列出了 tag项的每一种有效字符,还列出了每个字符所表示的值类型,以及value联合体中与该字符相对应的项。在讲解value联合体中的每个项目时,我们会在该项下方的描述信息中用到表格的第4列。

表4-11 tag值的含义及其所表示的类型

tag项 类型 value联合体中的项 常量类型
B byte const_value_index CONSTANT_Integer
C char const_value_index CONSTANT_Integer
D double const_value_index CONSTANT_Double
F float const_value_index CONSTANT_Float
I int const_value_index CONSTANT_Integer
J long const_value_index CONSTANT_Long
S short const_value_index CONSTANT_Integer
Z boolean const_value_index CONSTANT_Integer
s String const_value_index CONSTANT_Utf8
e 枚举类型 enum_const_value 不适用
c Class class_info_index 不适用
@ 注解类型 annotation_value 不适用
[ 数组类型 array_value 不适用

value项表示键值对中的值。此值是个联合体,它里面的各项如下:

  • const_value_index
    如果使用联合体中的 const_value_index 项,那就表示键值对里的值是个原始类型的常量值或String类型的字面量。
    const_value_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员,其类型必须与tag项相符
    ,如表4-11所示。
  • enum_const_value
    如果使用联合体中的 enum_const_value 项,那就表示键值对里的值是个枚举常量
    enum_const_value 项包含如下两项:
    • type_name_index
      type_name_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示一个有效的字段描述符(见4.3.2小节),这个字段描述符给出了当前element_value结构所表示的枚举常量类型的二进制名称的内部形式(见4.2.1小节)。
    • const_name_index
      const_name_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,此结构给出了当前element_value结构所表示的枚举常量的简单名称。
  • class_info_index
    如果使用联合体中的 class_info_index 项,那就表示键值对里的值是个类字面量(class literal)。
    class_info_index 项必须是指向常量池表项的有效索引。该索引处的常量池项必须是CONSTANT_Utf8_info结构(见4.4.7小节),用以表示返回描述符(见4.3.3小节)。返回描述符给出了与该element_value结构所表示的类字面量相对应的类型。类型与字面量的对应关系如下:
    • 如果类字面量是C.class,且C是类、接口或数组类型的名字,那么对应的类型就是C。常量池中的返回描述符会是ObjectType 或 ArrayType。
    • 如果类字面量是p.class,且p是原始类型的名称,那么对应的类型就是p。 常量池中的返回描述符会是一个BaseType字符。
    • 如果类字面量是void.class,那么对应的类型就是void。常量池中的返回描述符会是V。
      例如,类字面量Object.class对应于类型Object,因此常量池项就是 Ljava/lang/Object;,而类字面量 int.class 对应于类型int,所以常量池项就是I。类字面量void.class对应于void,因此常量池项是V,而类字面量Void.class对应于类型Void,所以常量池项是Ljava/lang/Void;
  • annotation_value
    如果使用联合体中的annotation_value项,那就表示键值对里的值本身又是个注解。
    annotation_value 项的值是个annotation结构(见4.7.16节),它给出了当前这个element_value结构所表示的注解。
  • array_value
    如果使用联合体中的array_value项,那就表示键值对里的值是个数组。
    array_value项包含如下两项:
    • num_values
      num_values项的值给出了由当前element_value结构所表示的数组的成员数量。
    • values
      values表的每个成员的值对应了当前element_value结构所表示的数组中的一个元素。

4.7.17 RuntimelnvisibleAnnotations 属性

RuntimelnvisibleAnnotations 属性是变长属性,位于ClassFile(见4.1节)、field_info(见4.5节)或method_info (见4.6节)结构的属性表中。它用于保存Java 语言中标注在类、方法或字段声明上面的运行时非可见注解。

每个ClassFile、field_info和method_info结构的属性表中最多只能含有一个 RuntimeInvisibleAnnotations 属性

RuntimelnvisibleAnnotations 属性和 RuntimeVisibleAnnotations 属性(见4.7.16小节)相似,但不同的是,RuntimelnvisibleAnnotations表示的注解不能被反射API访问,除非Java虚拟机通过与实现相关的特殊方式(比如特定的命令行标志参数)保留这些注解。否则,Java虚拟机将忽略RuntimelnvisibleAnnotations属性

RuntimelnvisibleAnnotations 属性的格式如下:

RuntimelnvisibleAnnotations_attribute {
	u2	attribute_name_index;
	u4	attribute_length;
	u2	num_annotations;
	annotation annotations[num_annotations];
}

RuntimeInvisibleAnnotations_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用来表示字符串 “RuntimeInvisibleAnnotations”
  • attribute_length
    attribute_length 项的值给出了当前属性的长度,不包括初始的6个字节。
  • num_annotations
    num_annotations 项的值给出了当前结构所表示的运行时不可见注解的数量。
  • annotations
    annotations 表中的每一项都表示标注在声明上面的一条运行时不可见注解。annotation结构定义在4.7.16小节中。

4.7.18 RuntimeVisibleParameterAnnotations 属性

RuntimeVisibleParameterAnnotations 属性是变长属性,位于 method_info(见4.6节)结构的属性表中。RuntimeVisibleParameterAnnotations 属性记录了标注在对应方法的形式参数声明上面的运行时可见注解。Java虚拟机必须保证这些注解可供取用,以便令合适的反射API能够将它们返回给调用者。

每个method_info结构的属性表中最多只能包含一个RuntimeVisibleParameterAnnotations属性

RuntimeVisibleParameterAnnotations 属性的格式如下:

RuntimeVisibleParameterAnnotations_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	ul num_parameters;
	{	u2 num_annotations;
		annotation annotations[num_annotations];
	} parameter_annotations[num_parameters];
}

RuntimeVisibleParameterAnnotations_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示字符串 “RuntimeVisibleParameterAnnotations”
  • attribute_length
    attribute_length 项的值给出了当前属性的长度,不包括初始的6个字节
  • num_parameters
    num_parameters 项的值给出了由method_info结构所表示的方法中形式参数的个数,注解正是添加在这个方法的参数上面。此信息也可从方法描述符中查出
  • parameter_annotations
    parameter_annotations 表中的每个成员都表示一个形式参数的所有运行时可见注解。parameter_annotations表中的第 i 项,对应于方法描述符中的第 i 个形式参数(见4.3.3小节)。每个parameter_annotations成员都包含如下两项:
    • num_annotations
      num_annotations项的值,表示标注在与当前这个 parameter_annotations 项相对应的那个形式参数声明上面的运行时可见注解个数
    • annotations
      annotations表中的每一项,都表示标注在与当前这个 parameter_ annotations 项相对应的那个形式参数声明上面的一条运行时可见注解。 annotation结构参见4.7.16小节。

4.7.19 RuntimelnvisibleParameterAnnotations 属性

RuntimelnvisibleParameterAnnotations 属性是变长属性,位于method_info(见4.6节)结构的属性表中。它用于保存标注在对应方法的形式参数声明上面的运行时不可见注解。

每个method_info结构的属性表中最多只能含有一个RuntimelnvisibleParameterAnnotations 属性。

RuntimelnvisibleParameterAnnotations属性和RuntimeVisibleParameterAnnotations属性(见4.7.18小节)类似,区别是RuntimelnvisibleParameterAnnotations属性表示的注解不能被反射的API访问,除非Java虚拟机通过与实现相关的特殊方式(比如特定的命令行标志参数)保留这些注解。否则,Java虚拟机将忽略RuntimelnvisibleParameterAnnotations属性

RuntimelnvisibleParameterAnnotations 属性的格式如下:

RuntimelnvisibleParameterAnnotations_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u1 num_parameters;
	{ 	u2 num_annotations;
		annotation annotations[num_annotations];
	} parameter_annotations [num_parameters];
}

RuntimeInvisibleParameterAnnotations_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示字符串 “RuntimeInvisibleParameterAnnotations”
  • attribute_length
    attribute_length 项的值给出了当前属性的长度,不包括初始的6个字节
  • num_parameters
    num_parameters 项的值给出了由method_info结构所表示的方法中形式参数的个数,注解正是添加在这个方法的参数上面。此信息也可从方法描述符中查出
  • parameter_annotations
    parameter_annotations 表中的每个成员都表示一个形式参数的所有运行时非可见注解。parameter_annotations 表中的第 i 项,对应于方法描述符中的第 i 个形式参数(见4.3.3小节)。每个parameter_annotations成员都包含如下两项:
    • num_annotations
      num_annotations 项的值,表示标注在与当前这个parameter_annotations项相对应的那个形式参数声明上面的运行时非可见注解个数
    • annotations
      annotations表中的每一项,都表示标注在与当前这个parameter_annotations项相对应的那个形式参数声明上面的一条运行时不可见注解。annotation结构参见4.7.16小节。

4.7.20 RuntimeVisibleTypeAnnotations 属性

RuntimeVisibleTypeAnnotations属性是ClassFile(见4.1节)、field_info(见4.5节)、method_info(见4.6节)结构或 Code 属性(见4.7.3小节)attributes 表中的变长属性。 RuntimeVisibleTypeAnnotations属性记录了标注在对应类声明、字段声明或方法声明所使用的类型上面的运行时可见注解,也记录了标注在对应方法体中某个表达式所使用的类型上面的运行时可见注解。此外,它还记录了标注在泛型类、接口、方法及构造器的类型参数声明上面的运行时可见注解。Java虚拟机必须使这些注解可供取用,以便令合适的反射API能够将它们返回给调用者

在ClassFile结构、field_info结构、method_info结构或Code属性的属性表中,最多只能有1个RuntimeVisibleTypeAnnotations 属性

只有当某个属性表的上级结构体或上级属性所对应的声明或表达式中带有加了注解的类型时,该属性表才可以包含RuntimeVisibleTypeAnnotations属性

例如,对某个类声明的implements子句里的各类型所加的全部注解,都会记录在该类ClassFile结构体的 RuntimeVisibleTypeAnnotations 属 性 里。而对某个字段声明中的类型所加的全部注解,则会记录在该字段field_info结构体的 RuntimeVisibleTypeAnnotations 属性里。

RuntimeVisibleTypeAnnotations 属性的格式如下:

RuntimeVisibleTypeAnnotations_attribute {
	u2	attribute_name_index;
	u4	attribute_length;
	u2	num_annotations;
	type_annotation annotations[num_annotations];
}

RuntimeVisibleTypeAnnotations_attribute 结构体的各项,其含义如下:

  • attribute_name_index
    attribute_name_index 项的值必须是指向常量池表项的有效索引。该索引处的常量池项,必须是个CONSTANT_Utf8_info结构,用以表示字符串 “RuntimeVisibleTypeAnnotations”
  • attribute_length
    attribute_length 项的值表示本属性的长度,它不包括开头的6个字节
  • num_annotations
    num_annotations 项的值,给出了本结构所代表的运行时可见的类型注解(runtime visible type annotations) 的数量
  • annotations [ ]
    annotations 表中的每项都表示标注于声明或表达式所使用的类型上面的一条运行时可见注解

type_annotation结构体的格式如下:

type_annotation {
	u1 target_type;
	union {	.
		type_parameter_target;
		supertype_target;
		type_parameter_bound_target;
		empty_target;
		method_formal_parameter_target;
		throws_target;
		localvar_target;
		catch_target;
		offset_target;
		type_argument_target;
	} target_info;
	type_path target_path;
	u2	type_index;
	u2	num_element_value_pairs;
	{	u2	element_name_index;
		element_value value;
	} element_value_pairs[num_element_value_pairs];
} 

type_annotation 的前 3 项,也就是 target_type、target_info及target_ path,指出了带注解的类型所在的精确位置。而后3项,也就是type_index、num_element_value_pairs、element_value_pairs [ ],则指出了注解本身的类型及键值对。

type_annotation 结构各项的含义如下:

  • target_type
    target_type 项的值指明了注解的目标是何种类。有很多种目标对应于Java语言的各种类型语境(type context),而 RuntimeVisibleTypeAnnotations 属性所描述的注解,就添加在某种类型语境中的声明及表达式所使用的类型上面(JLS §4.11 )。

表4-12与表4-13列出了target_type的合法取值。每个值都是长度为1个字节的标记,用来指出当前使用的是target_type之后那个target_info联合体里的哪一项。联合体中正在使用的那一项给出了与注解目标有关的详细信息。

表4-12与表4-13中的每一种目标,对应于JLS §4.11里的一个类型语境。也就是说,值在0x10 ~ 0x17及0x40 ~ 0x42之间的target_type,对应于1 ~ 10号目标语境,而值在0x43 ~ 0x4B之间的target_type,则对应于11至15号类型语境。

target_type 项决定了 RuntimeVisibleTypeAnnotations 属性中的 type_ annotation 结构应该出现在 ClassFile 结构、field_info 结构、method_ info 结构还是Code属性中。表4-14指出了每个合法的target_type值所在的 type_annotation 结构体应该出现在谁的 RuntimeVisibleTypeAnnotations 属性之中。

  • target_info
    target_info 项的值准确地指出了本条注解添加在声明或表达式中的哪个类型上面。
    target_info 联合体的各项,列在4.7.20.1小节里。
  • target_path
    target_path 项的值准确地指出了本条注解添加在由target_info所指出的类型的哪一部分上面。
    type_path 结构体的格式,列在4.7.20.2小节里。
  • type_index、num_element_value_pairs 及 element_value_pairs [ ]
    type_annotation 结构体里面的这些项,其含义与它们在annotation结构体中的含义(见4.7.16小节)相同。

表4-12 target_type值的含义(第1部分)

目标的种类 target_info 项
0x00 泛型类或接口的类型参数声明 type_parameter_target
0x01 泛型方法或构造器的类型参数声明 type_parameter_target
0x10 类或接口声明(也包括匿名类声明中的直接超类声明)中的extends子句里的类型,或者接口声明中的implements子句里的类型 supertype_target
0x11 在声明泛型类或接口的类型参数界限时,所用到的类型 type_parameter_bound_target
0x12 在声明泛型方法或构造器的类型参数界限时,所用到的类型 type_parameter_bound_target
0x13 字段声明中的类型 empty_target
0x14 方法的返回值类型,或者新构建好的对象的类型 empty_target
0x15 方法或构造器的接收者(receiver)类型 empty_target
0x16 方法、构造器或lambda表达式的形式参数声明中的类型 formal_parameter_target
0x17 方法或构造器throws子句中的类型 throws_target

表4-13 target_type值的含义(第2部分)

目标的种类 target_info 项
0x40 局部变量声明中的类型 localvar_target
0x41 资源变量声明中的类型 localvar_target
0x42 异常参数声明中的类型 catch_target
0x43 instanceof 表达式中的类型 offset_target
0x44 new 表达式中的类型 offset_target
0x45 以 ::new 的形式来表述的方法引用表达式中的类型 offset_target
0x46 以 ::Identifier 的形式来表述的方法引用表达式中的类型 offset_target
0x47 类型转换表达式中的类型 type_argument_target
0x48 表达式中的泛型构造器或显式构造器调用语句中的类型参数 type_argument_target
0x49 方法调用表达式中的泛型方法的类型参数 type_argument_target
0x4A 在以 ::new的形式来表述的方法引用表达式中,泛型构造器的类型参数 type_argument_target
0x4B 在以 ::Identifier的形式来表述的方法引用表达式中,泛型方法的类型参数 type_argument_target

表 4-14 target_type值所在的 RuntimeVisibleTypeAnnotations 属性应该出现的位置

目标的种类 位置
0x00 泛型类或接口的类型参数声明 ClassFile
0x01 泛型方法或构造器的类型参数声明 method_info
0x10 类或接口声明中的extends子句里的类型,或者接口声明中的 implements子句里的类型 ClassFile
0x11 在声明泛型类或接口的类型参数界限时,所用到的类型 ClassFile
0x12 在声明泛型方法或构造器的类型参数界限时,所用到的类型 method_info
0x13 字段声明中的类型 field_info
0x14 方法或构造器的返回值类型 method_info
0x15 方法或构造器的接收者类型 method_info
0x16 方法、构造器或lambda表达式的形式参数声明中的类型 method_info
0x17 方法或构造器throws子句中的类型 method_info
0x40-0x4B 局部变量声明、资源变量声明、异常参数声明及表达式中所用到的类型 Code

4.7.20.1 target_info 联合体

target_info联合体中的项精确地指出了本条注解添加在声明或表达式中的哪个类型上面。然而第一项却例外,它不是指出声明或表达式中的某个类型,而是指出哪一个类型参数的声明上面添加了注解。

联合体中各项的含义如下:

  • 如果使用 target_info 联合体中的 type_parameter_target 项,那就意味着:本条注解添加在泛型类、泛型接口、泛型方法或泛型构造器的第 i 个类型参数声明上面。
type_parameter_target {
	u1 type_parameter_index;
}

type_parameter_index项的值指出了本条注解添加在哪个类型参数的声明上面。 它的值如果0,那就表示本条注解添加在首个类型参数声明上面。

  • 如果使用 target_info 联合体中的 supertype_target 项,那就意味着:本条注解添加在类声明或接口声明的extends子句或implements子句里的某个类型上面。
supertype_target {
	u2 supertype_index;
}

super type_index的值如果是65535,那就表示本条注解添加在类声明的 extends子句中的那个超类名称上。
除了65535之外的其他supertype_index值,都是对外围ClassFile结构 interfaces 数组的索引,该索引处的元素要么是类声明的 implements 子句中的某个超接口,要么是接口声明的extends子句中的某个超接口,本条注解就添加在那个超接口上面。

  • 如果使用 target_info 联合体中的 type_parameter_bound_target 项,那就意味着:本条注解添加在泛型类、泛型接口、泛型方法或泛型构造器第 j 个类型参数声明中的第 i 个界限上面。
type_parameter_bound_target {
	u1 type_parameter_index;
	u1 bound_index;
}

type_parameter_index 项的值指出了带有注解的那个界限是针对哪一个类型参数声明而言的。type_parameter_index的值如果是0,那就表示该界限针对的是首个类型参数声明。
bound_index 项的值指出了在由 type_parameter_index 所确定的那个类型参数声明中,究竟是哪一个界限上面添加了本条注解。bound_index 的值如果是0,那就表示本注解添加在类型参数声明中的首个界限上面。
type_parameter_bound_target 项可以说明某个界限添加了注解,但它并没有记录该界限所指的类型。此类型可以从保存在适当的Signature属性中的类签名或方法签名里面查出。

  • 如果使用 target_info 联合体中的 empty_target 项,那就意味着:本注解添加在字段声明、方法的返回值类型、新构造的对象的类型或是方法或构造器的接收者类型上面。
empty_target {
}

由于这些位置上面都只会出现一个类型,所以当 target_info 联合体表示的是 empty_target 时,不需要再给出类型语境中每个类型的信息。

  • 如果使用 target_info 联合体中的 formal_parameter_target 项,那就意味着:本注解添加在方法、构造器或 lambda表达式的形式参数声明中的类型上面。
formal_parameter_target {
	u1 formal_parameter_index;
}

formal_parameter_index 项的值指出了带有注解的类型位于哪一个形式参数声明之中。如果formal_parameter_index的值是0,那就表示该类型处在首个形式参数的声明之中。
formal_parameter_target 项可以说明某个形式参数的类型是带有注解的,但它并没有记录那个类型本身是什么。我们可以通过RuntimeVisibleTypeAnnotations属性外围的method_info结构中的方法描述符(见4.3.3小节)来检视该类型。 formal_parameter_index的值如果是0,那就表示该类型由方法描述符中的首个参数描述符来描述。

  • 如果使用 target_info 联合体中的 throws_target 项,那就意味着:本注解添加在方法声明或构造器声明的 throws 子句所提到的第 i 个类型上面。
throws_target {
	u2 throws_type_index;
}

throws_type_index 项的值是对 exception_index_table 数组的索引,该数组位于RuntimeVisibleTypeAnnotations 属性外围的 method_info 结构中的Exceptions属性里。

  • 如果使用 target_info 联合体中的 localvar_target 项,那就意味着:本注解添加在局部变量声明中的类型上面(这里所说的局部变量,也包括声明在 try-with-resources 语句结构中的资源变量)。
localvar_target {
	u2 table_length;
	{
		u2 start_pc;
		u2 length;
		u2 index;
	} table[table_length];
}

table_length 项的值给出了table数组中的元素个数。table数组的每个元素都以字节码在code数组中的偏移量来限定某个范围,此局部变量在这个范围内是有值的。数组元素还指出了此局部变量在当前帧的局部变量表中的索引。table数组的每个元素含有下列3项:
start_pc 和 length
当给定的局部变量位于code数组的 [start_pc,start_pc + length )范围内,也就是处在由偏移量大于等于 start_pc 且小于 start_pc + length 的字节码所构成的范围内时,该局部变量是有值的。
index
在当前帧的局部变量表中,给定的局部变量必定位于索引为index的位置上。
如果index所指的局部变量是double型或long型,那么该局部变量会占据 index 及 index + 1 这两个位置。

一个局部变量是可以有多个活跃范围(live range)的,而这些范围可以用局部变量表中的多个索引项来表示㊀,因此,其类型添加了注解的局部变量,必须用一整张表格才能完全表述出来。表格中每个元素的start_pc、length及index项所 包含的信息,与LocalVariableTable属性中的这些项所包含的信息相同。
localvar_target 项可以说明局部变量的类型加了注解,但它并没有记录这个类型本身。我们可从适当的LocalVariableTable属性中查出该类型。

㊀ 这种情况的详细信息,可参阅《Type Annotations Specification (JSR 308) 》( http://types.cs.washington.edu/ jsr308/specification/java-annotation-design.html)的 3.3.7 小节。	——译者注
  • 如果使用 target_info 联合体中的 catch_target 项,那就意味着:本注解添加在异常参数声明中的第 i 个类型上面。
catch_target {
	u2 exception_table_index;
}

exception_table_index 项的值是对 exception_table 数组的索引,该数组位于 RuntimeVisibleTypeAnnotations 属性外围的 Code 属性中。
如果try语句带有multi-catch子句㊁,那么异常参数声明里面就会出现多个类型, 此时,异常参数的类型就是这些类型的并集(参见JLS § 14.20 )。对于并集中的每个类型来说,编译器通常会在exception_table中创建与之对应的元素,而我们可以通过catch_target项中的exception_table_index来区分这些元素。如此一来,就能保持类型与注解之间的对应关系了。

㊁ 可以同时捕获多种异常的 catch 子句,例40: catch (ClassNotFoundException | IllegalAccess- 
Exception ex) { . . . } 。——译者注
  • 如果使用 target_info 联合体中的 offset_target 项,那就意味着:本注解要么添加在 instanceof 表达式或 new 表达式的类型上面,要么添加在方法引用表达式的 :: 符号前方的类型上面。
offset_target {
	u2 offset;
}

offset 项的值用来描述与 instanceof 表达式相对应的 instanceof 字节码指令在 code 数组里的偏移量、与new表达式相对应的new字节码指令在code数组里的偏移量,或是与方法引用表达式相对应的字节码指令在code数组里的偏移量。

  • 如果使用 target_info 联合体中的 type_argument_target 项,那就意味着:本条注解要么添加在类型转换表达式的第 i 个类型上面,要么添加在显式类型参数列表的第 i 个类型参数上面。这种参数列表针对的是new表达式、显式构造器调用语句、方法调用表达式或方法引用表达式。
type_argument_target {
	u2 offset;
	u1 type_argument_index;
}

offset 项的值是某条字节码指令相对于code数组的偏移量。该指令可能是与类型转换表达式相对应的指令、与new表达式相对应的new指令、与显式构造器调用语句相对应的指令、与方法调用表达式相对应的指令,或与方法引用表达式相对应的指令。 对于类型转换表达式来说,type_argument_index 项的值表示本条注解添加在类型转换操作符里的哪个类型上面。type_argument_index 的值如果是0,那就表示本注解添加在类型转换操作符的首个(或唯一的那个)类型上面。

在向交集类型(intersection type)转换的时候,转型表达式中会出现多个类型。

对于显式类型参数列表来说,type_argument_index 项的值表示本条注解添加在哪个类型参数上面。type_argument_index 的值如果是0,那就表示添加在首个类型参数上。

4.7.20.2 type_path 结构体

对于声明或表达式里面用到的某个类型来说,type_path 结构体可以指出该类型的哪一部分加了注解。注解可能会直接添加到该类型本身,但如果此类型是个引用类型的话,那么注解还可以添加在该类型的其他位置上:

  • 如果声明或表达式里用到了数组类型 T [ ],那么注解可以添加在它的任何组件类型上面,这其中也包括该数组类型的元素类型。
  • 如果声明或表达式里用到了嵌套类型T1.T2,那么注解既可以添加在顶级类型的名称上面,也可以添加在任何成员类型的名称上面。
  • 如果声明或表达式里用到了参数化类型 T< A >、T< ? extends A >或 T< ? super A >,那么注解既可以添加在类型参数上,也可以添加在任何通配符类型参数 (wildcard type argument)的边界上。

例如,String[ ][ ] 这个类型的不同部分可以分别添加注解:

@Foo String[][]		//	Annotates the class	type String
String @Foo[] []	//	Annotates the array	type String[][]
String [] @Foo []	//	Annotates the array	type String[]

Outer.Middle.Inner 这个嵌套类型的不同部分可以分别添加注解:

@Foo Outer.Middle.Inner 
Outer.@Foo Middle.Inner
Outer.Middle.@Foo Inner

参数化类型 Map 及 List< … > 的不同部位,也可以分别添加注解:

@Foo Map<String, Object> 
Map<@Foo String, Object> 
Map<String, @Foo Object>

List<@Foo ? extends String> 
List<? extends @Foo String> 

type_path 结构的格式如下:

type_path {
	u1 path_length;
	{	u1 type_path_kind; 
		u1 type_argument_index;
	} path[path_length];
}

path_length 项的值给出了path数组中的元素个数:

  • 如果path_length的值是0,那就表示注解是直接添加在类型本身上面的。
  • 如果path_length的值不是0,那么path数组中的每个元素就从左至右逐步深入地指出本条注解在数组类型、嵌套类型或参数化类型里的精确位置。(对于数组类型来说,先看注解是不是添加在数组类型自身上面,然后看是不是加在它的组件类型上面,接下来看是不是加在组件类型的组件类型上面,依此类推,直到元素类型为止。) path数组里的每个元素含有下列两项:
    type_path_kind
    type_path_kind 项的合法取值列在表4-15之中。

表4-15 各种type_path_kind取值的含义

含义
0 注解位于数组类型的深处
1 注解位于嵌套类型的深处
2 注解添加在参数化类型中某个通配符类型参数的边界上
3 注解添加在参数化类型中的某个类型参数上

type_argument_index
type_path_kind 项的值如果是0、1或2,那么type_argument_index 项的值就是0。
type_path_kind 项的值如果是3,那么type_argument_index项的值会指出参数化类型中带有注解的那个类型参数,0表示本条注解添加在参数化类型中的首个类型参数上面。

表4-16 以@A Map<@B ? extends @C String, @D List<@E Object>> 中的各注解为例来演示type_path结构体

注解 path_length path
@A 0 [ ]
@B 1 [(type_path_kind: 3; type_argument_index: 0}]
@C 2 [(type_path_kind: 3; type_argument_index: 0 }, (type_path_ kind: 2; type_argument_index: 0}]
@D 1 [(type_path_kind: 3; type_argument_index: 1)]
@E 2 [{type_path_kind: 3; type_argument_index: 1), {type_path_ kind: 3; type_argument_index: 0}]

表4-17 以@I String @F [ ] @G [ ] @H [ ] 中的各注解为例来演示type_path结构体

注解 path_length path
@F 0 [ ]
@G 1 [(type_path_kind: 0; type_argument_index: 0)]
@H 2 [(type_path_kind: 0; type_argument_index: 0 ), {type_path_ kind: 0; type_argument_index: 0)]
@I 3 [{type_path_kind: 0; type_argument_index: 0 ), {type_path_ kind : 0 ; type_argument_index: 0 } , (type_path_kind: 0 ; type_argument_index: 0)]

表4-18 以@A List<@B Comparable<@F Object @C [] @D [] @E []>>中的各注解为例来演示type_path结构体

注解 path_length path
@A 0 [ ]
@B 1 [{type_path_kind: 3; type_argument_index: 0)]
@C 2 [(type_path_kind: 3; type_argument_index: 0 }, (type_path_ kind: 3; type_argument_index: 0)]
@D 3 [(type_path_kind: 3; type_argument_index: 0 }, (type_path_ kind : 3 ; type_argument_index: 0 ) , {type_path_kind: 0 ;type_argument_index: 0}]
@E 4 [(type_path_kind: 3 ; type_argument_index : 0 } , {type_path_kind: 3; type_argument_index: 0 ),{type_path_kind:0 ; type_argument_index: 0 ) , {type_path_kind: 0 ; type_ argument_index: 0}]
@F 5 [(type_path_kind: 3 ; type_argument_index: 0 ) , (type_path_kind: 3; type_argument_index: 0 ),(type_path_kind: 0 ; type_argument_index: 0 ) , {type_path_kind: 0; type_argument_index: 0 ), {type_path_kind: 0; type_argument_ index : 0}]

表4-19 以@C Outer . @B Middle . @A Inner 中的各注解为例来演示type_path结构体

注解 path_length path
@A 2 [(type_path_kind: 1; type_argument_index: 0 }, (type_path_ kind: 1; type_argument_index: 0)]
@B 1 [(type_path_kind: 1; type_argument_index: 0}]
@C 0 [ ]

表4-20 以Outer . Middle<@D Foo . @C Bar> . Inner<@B String @A [ ] > 中的各注解为例来演示type_path结构体

注解 path_length path
@A 3 [(type_path_kind: 1; type_argument_index: 0 ), {type_path_ kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_ argument_index: 0}]
@B 4 [(type_path_kind: 1; type_argument_index: 0 }, (type_path_ kind: 1; type_argument_index: 0}, {type_path_kind: 3; type_ argument_index: 0 ) , (type_path_kind: 0 ; type_argument_ index: 0}]
@C 3 [(type_path_kind: 1; type_argument_index: 0 }, (type_path_ kind: 3; type_argument_index: 0), (type_path_kind: 1; type_ argument_index: 0)]
@D 2 [(type_path_kind: 1; type_argument_index: 0 ), (type_path_ kind: 3; type_argument_index: 0}]

4.7.21 RuntimelnvisibleTypeAnnotations 属性

RuntimelnvisibleTypeAnnotations 属性是ClassFile(见4.1节)、field_info(见4.5节)、method_info(见4.6节)结构或 Code 属性(见4.7.3小节)attributes 表中的变长属性。RuntimelnvisibleTypeAnnotations属性记录了标注在对应类声明、字段声明或方法声明所使用的类型上面的运行时不可见注解,也记录了标注在对应方法体中某个表达式所使用的类型上面的运行时不可见注解。此外,它还记录了标注在泛型类、 接口、方法及构造器的类型参数声明上面的运行时不可见注解。

在ClassFile结构、field_info结构、method_info结构或Code属性的属性表中,最多只能有1个 RuntimeInvisibleTypeAnnotations 属性。

只有当某个属性表的上级结构体或上级属性所对应的声明或表达式中带有加了注解的类型时,该属性表才可以包含RuntimeInvisibleTypeAnnotations属性。

RuntimeInvisibleTypeAnnotations 属性的格式如下:

RuntimelnvisibleTypeAnnotations_attribute {
	u2	attribute_name_index;
	u4	attribute_length;
	u2	num_annotations;
	type_annotation annotations[num_annotations];
} 

RuntimelnvisibleTypeAnnotations_attribute 结构体的各项,其含义如下:

  • attribute_name_index
    attribute_name_index 项的值必须是指向常量池表项的有效索引。该索引处的常量池项,必须是个CONSTANT_Utf8_info结构,用以表示字符串 "RuntimelnvisibleTypeAnnotations_attribute " 。
  • attribute_length
    attribute_length 项的值表示本属性的长度,它不包括开头的6个字节。
  • num_annotations
    num_annotations 项的值,给出了本结构所代表的运行时不可见的类型注解 (runtime invisible type annotations)的数量。
  • annotations [ ]
    annotations表中的每项,都表示标注于声明或表达式所使用的类型上面的一条运行时不可见注解。type_annotation结构体的格式定义在4.7.20小节中。

4.7.22 AnnotationDefault 属性

AnnotationDefault属性是个长度可变的属性,它出现在某些method_info结构体(见4.6节)的属性表里,而那种method_info结构体,则用来表示注解类型中的元素 (JLS § 9.6.1 )。 AnnotationDefault属性记录了由method_info结构所表示的那个元素的默认值(JLS § 9.6.2 )。Java虚拟机必须令默认值可供取用,以便使合适的反射API能够将其提供给调用者。

如果某个method_info结构体用来描述注解类型里的元素,那么该结构体的属性表中最多只能有1个AnnotationDefault属性。

AnnotationDefault属性的格式如下:

AnnotationDefault_attribute {
	u2	attribute_name_index;
	u4	attribute_length;
	element_value default_value;
}

AnnotationDefault_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info (见4.4.7小节)结构,用以表示字符串 “AnnotationDefault”。
  • attribute_length
    attribute_length 项的值给出了当前属性的长度,不包括初始的6个字节。
  • default_value
    default_value 项表示由AnnotationDefault属性外围的 method_info 结构所描述的那个注解类型元素的默认值。

4.7.23 BootstrapMethods 属性

BootstrapMethods属性是变长属性,位于ClassFile(见4.1节)结构的属性表中。它用于保存由 invokedynamic 指令(见6.5节的invokedynamic小节)引用的引导方法限定符。

如果某个ClassFile结构的常量池表中有至少一个CONSTANT_InvokeDynamic_info(见4.4.10小节)成员,那么这个ClassFile结构的属性表就必须包含,且只能包含一个 BootstrapMethods 属性。

ClassFile结构的属性表中最多只能有一个BootstrapMethods属性。

BootstrapMethods属性的格式如下:

BootstrapMethods_attribute {
	u2 attribute_name_index;
	u4 attribute_length;
	u2 num_bootstrap_methods;
	{ 	u2 bootstrap_method_ref;
		u2 num_bootstrap_arguments;
		u2 bootstrap_arguments[num_bootstrap_arguments];
	} bootstrap_methods[num_bootstrap_methods];
} 

BootstrapMethods_attribute 结构各项的说明如下:

  • attribute_name_index
    attribute_name_index 项的值必须是对常量池表的一个有效索引。常量池在该索引处的成员必须是CONSTANT_Utf8_info(见4.4.7小节)结构,用以表示字符串 “BootstrapMethods”。
  • attribute_length
    attribute_length 项的值给出了当前属性的长度,不包括初始的6个字节。
    attribute_length 项的值由ClassFile结构中 invokedynamic 指令的数量决定。
  • num_bootstrap_methods
    num_bootstrap_methods 项的值给出了 bootstrap_methods 数组中的引导方法限定符的数量。
  • bootstrap_methods [ ]
    bootstrap_methods [ ] 数组的每个成员包含一个指向 CONSTANT_MethodHandle_ info(见4.4.8小节)结构的索引值,该结构指明了一个引导方法,并指明了一个由索引组成的序列(可能是空序列),此序列里的索引指向该引导方法的静态参数(static argument )。

bootstrap_methods [ ] 数组每个成员必须包含以下3项:

  • bootstrap_method_ref
    bootstrap_method_ref 项的值必须是对常量池表的一个有效索引。常量池在该索引处的值必须是一个CONSTANT_MethodHandle_info(见4.4.8小节) 结构。

方法句柄的形式,由 invokedynamic(参见6.5节的invokedynamic小节)指令中调用点限定符(call site specifier)的持续解析过程来决定,java.lang.invoke.MethodHandle 类的 invoke 方法在执行的时候,要求引导方法的句柄必须能按传入的实际参数做出调整,就好似通过 java.lang.invoke.MethodHandle.asType 来调用一般。与之相应,CONSTANT_MethodHandle_info 结构 reference_kind 项的值应该是6或8(见5.4.3.5子小节),而 reference_index 项则应指明一个静态方法或构造器,它依次接受三个类型分别为 java.lang.invoke.MethodHandles.Lookup、String 及java.lang.invoke.MethodType 的参数。如果不符合上述要求,那么在调用点限定符的解析过程中,对引导方法句柄的调用就会失败。

  • num_bootstrap_arguments
    num_bootstrap_arguments 项的值给出了 bootstrap_arguments 数组的元素个数。
  • bootstrap_arguments
    bootstrap_arguments 数组的每个成员必须是对常量池表的一个有效索引。常量池表在该索引处必须是下列结构之一:CONSTANT_String_info(见4.4.3小节)、CONSTANT_Class_info(见4.4.1小节)、CONSTANT_Integer_info(见 4.4.4小节)、CONSTANT_Long_info(见4.4.5小节)、CONSTANT_Float_info(见4.4.4小节),CONSTANT_Double_info(见4.4.5小节)、CONSTANT_MethodHandle_info(见4.4.8小节)或 CONSTANT_MethodType_info(见4.4.9小节)。

4.7.24 Methodparameters 属性

Methodparameters属性是method_info结构(见4.6节)属性表中的变长属性。 MethodParameters属性记录了与形式参数有关的信息,例如参数名称等等。

method_info结构的属性表中,最多只能有1个MethodParameters属性。

MethodParameters属性的格式如下:

MethodParameters__attribute {
	u2 attribute_name_index;
	u4 attribute_length; 
	u1 parameters_count; 
	{ 	u2 name_index;
		u2 access_flags;
	} parameters[parameters count];
}

MethodParameters_attribute 结构各项的含义如下:

  • attribute_name_index
    attribute_name_index 项的值必须是指向常量池表的有效索引。常量池在该索引处的项,必须是个CONSTANT_Utf8_info结构,用以表示字符串 “MethodParameters”。
  • attribute_length
    attribute_length 项的值给出了该属性的长度,此长度不计算前6个字节。
  • parameters_count
    parameters_count 项的值指出了在由本属性外围method_info结构里的 descriptor_index 所引用的那个方法描述符中,有多少个参数描述符。
    Java虚拟机实现在执行格式检查(见4.8节)时,并不一定要保证该项的正确性。检查方法描述符里的参数描述符与下面要介绍的parameters数组里的项是否匹配,那是Java SE平台反射库的任务。
  • parameters [ ]

parameters 数组中的每个元素都包含下列两项:

  • name_index
    name_index 项的值要么是0,要么是指向常量池表的有效索引。
    如果name_index项的值是0,那就表示parameters数组里的这个元素,描述的是个没有名称的形式参数。
    如果name_index项的值不是0,那么该索引处的常量池项必须是个CONSTANT_Utf8_info结构,此结构表示一个有效的非限定名,用来指代某个形式参数(见4.2.2小节)。
  • access_flags
    access_flags 项的各种取值,其含义如下:
    0x0010 (ACC_FINAL)
    表示这个形式参数声明为final。
    0x1000 (ACC_SYNTHETIC)
    表示这个形式参数并没有显式或隐式地声明在源代码中,此判断是根据编写源代码所用的编程语言的规范书做出的(JLS § 13.1 )。 (也就是说,这个形式参数由制作这份class文件的编译器生成。)
    0x8000 (ACC_MANDATED)
    表示这个形式参数隐式地声明在源代码中,此判断是根据编写源代码所用的编程语言的规范书做出的(JLS § 13.1 )(也就是说,某种编程语言的规范书,强制要求所有针对该语言的编译器都必须生成这个形式参数)。

parameters 数组的第 i 项,对应于外围方法的描述符里的第 i 个参数描述符。(由于方法描述符最多只能有255个参数,因此parameters_count项只需占用1字节即可。) 实际上,这就意味着parameters数组保存了本方法所有参数的信息。也可以使用另一种存储方式,那就是令parameters数组里的项指出与之相对应的参数描述符,但那样做会使MethodParameters属性过于复杂。

parameters数组的第 i 项,可能与外围方法Signature属性(如果有的话)的第 i 个类型,或外围方法参数注解中的第 i 个注解相对应,也可能不与之对应。

你可能感兴趣的:(Java虚拟机规范,jvm,java)