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

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 项的值给出了分配在当前方法引
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java核心技术系列:Java虚拟机规范Java SE 8)》由Oracle官方发布,Java虚拟机技术创建人撰写,国内资深Java技术专家翻译。书中基于全新Java SE 8,完整且准确地阐述Java虚拟机规范,是深度了解Java虚拟机Java语言实现细节的必读之作。   《Java核心技术系列:Java虚拟机规范Java SE 8)》共分7章。第1章从宏观的角度介绍了Java虚拟机Java的关系及发展历程;第2章概述Java虚拟机的整体架构,包括class文件格式、数据类型、原始类型、引用类型、运行时数据区、栈帧、浮点算法、异常等,这对理解本书后面的内容有重要帮助;第3章详述如何将Java语言编写的程序转换为Java虚拟机指令集,涉及常量、局 部变量、控制结构、算术运算、参数接收、方法调用、数组、操作数栈、异常处理、同步与注解等;第4章深入分析用来表示编译后的类和接口的class文件格式;第5章定义Java虚拟机启动以及类与接口的加载、链接和初始化过程;第6章阐释并列举Java虚拟机指令集;第7章提供一张以操作码值为索引的Java虚拟机操作码助记符表。   《Java核心技术系列:Java虚拟机规范Java SE 8)》完整而准确地阐释了Java虚拟机各方面的细节,围绕Java虚拟机整体架构、编译器、class文件格式、加载、链接与初始化、指令集等核心主题对Java虚拟机进行全面而深入的分析,深刻揭示Java虚拟机的工作原理。同时,书中不仅完整地讲述了由Java SE 8所引入的新特性,例如对包含默认实现代码的接口方法所做的调用,还讲述了为支持类型注解及方法参数注解而对class文件格式所做的扩展,并阐明了class文件中各属性的含义,以及字节码验证的规则。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值