访问标识(access_flag)
- 在常量池后,紧跟着访问标记,标记使用两个字节表示,用于识别一些类或接口层次的访问信息,包括这个Class是类还是接口,是否定义为public类型,是否定义为abstract类型,如果是类的话,是否声明为final等,各种访问标记如下:
- 类的访问权限通常为ACC_开头的常量
- 每一种类型的表示都是通过设置访问标记的32位中的特定位来实现的,比如,若是public final的类,则该标记为ACC_PUBLIC | ACC_FINAL
- 使用ACC_SUPER可以让类更准确地定位到父类的方法super.method(),现代编译器都会设置并且使用这个标记
- 带有ACC_INTERFACE标志的class文件表示是接口不是类,反之则表示是类而不是接口
- 如果一个class文件被设置了ACC_INTERFACE标志,那么同时也得设置ACC_ABSTRACT标志,同时不能设置ACC_FINAL、ACC_SUPER或ACC_ENUM标志
- 如果没有设置ACC_INTERFACE标志,那么这个class文件可以具有上表中除ACC_ANNOTATION外的其他所有标志,当然ACC_FINAL和ACC_ABSTRACT这类互斥的标志除外,这两个标志不能同时设置
- ACC_SUPER标志用于标志用于确定类或接口里面的invokespecial指令使用的哪一种执行语义,针对Java虚拟机指令集的编译器都应设置这个标志,对于Java SE8及后续版本来说,无论class文件中这个标志的实际值是什么,也不管class文件的版本是多少,Java虚拟机会为每个class文件均设置ACC_SUPER标志
- ACC_SUPER标志为了向后兼容由旧Java编译器所编译的代码而设计,目前的ACC_SUPER标志在由JDK1.0.2之前的编译器生成的access_flags中没有确定意义,如果设置该标志,oracle的JVM实现会将其忽略
- ACC_SYNTHETIC标志意味着该类或接口是由编译器生成的,而不是由源代码生成的
- 注解类型必须设置为ACC_ANNOTATION标志,如果设置了ACC_ANNOTATION标志,那么也必须设置ACC_INTERFACE标志
- ACC_ENUM标志表明该类或其父类为枚举类型
- 表中没有使用的Access_flags标志为未来扩充预留,这些预留标志在编译器中应该设置为0,JVM实现也应忽略他们
类索引、父类索引、接口索引集合
- 在访问标记后,会指定该类的类别、父类类别及实现的接口,格式如下
- 这三项数据来确认这个类的继承关系
- 类索引用于确定这个类的全限定名
- 类索引用于确定这个类的父类的全限定名,由于Java语言不允许多重继承,父类索引只有一个,除java.lang.Object除外,所有的Java类都有父类,除java.lang.Object外,所有Java类的父类索引都不为0
- 接口索引集合就用来描述这个类实现了哪些接口,这些被实现的接口按implements语句(如果类本身为接口,则应使用extends语句)后的接口顺序从左到右排列在接口索引集体中
类索引this_class
2字节无符号整数,指向常量池的索引,它提供了类的全限定名,如com/chapter09/Demo,this_class的值必须是对常量池表中某项的一个有效索引,常量池在这个索引处的成员都必须为CONSTANT_Class_info类型结构体,该结构表示这个class文件所定义的类或接口
父类索引super_class
- 2字节无符号整数,指向常量池的索引,它提供了父类的全限定名,如无继承关系,其默认继承java/lang/Object类,由于Java不支持多继承,其父类只有一个
- superclass指向的父类不能是final
interfaces
- 指向常量也索引集合,它提供了一个符号引用到所有已实现的接口
- 由于一个类可以实现多个接口,因此需要以数组形式保存多个接口的索引,表示接口的第个索引也是一个指向常量池的CONSTANT_Class(此处必须是接口,不是类)
- interfaces_count接口计数器
interfaces_count项的值表示当前类或接口的直接超接口数量 - interfaces[]接口索引集合
interfaces[]中每个成员的值必须是对常量池表中某项的有效索引值,它的长度为interfaces_count,每个成员interfaces[i]必须为CONSTANT_Class_info结构,其中0 <= i < interfaces_count,在interfaces[]中,各成员所表示的接口顺序和对应的源代码中给定的接口顺序一样,即interfaces[0]对应的是源代码中最左边的接口
字段表集合
- 用于描述接口或类声明的变量,字段(field),包括类级变量以及实例级变量,但不包括方法内部、代码块内部声明的局部变量
- 字段叫什么名字、字段定义的是什么数据类型,这此都是无法固定的,只能引用常量池中的常量来描述
- 它指向常量池索引集合,它描述了每个字段的完整信息,比如字段的标识符、访问修饰符(public,private,protected),是类变量还是实例变量(static修饰符),是否是常量(final修饰符)等
注: - 字段表集体中不会列出从父类或者实现接口中继承而来的字段,但有可能列出原本Java代码中不存在的字段,譬如在内部类中为了保持对外部类的访问性,会自动添加指向外部类实例的字段
- 在Java语言中字段是无法重载的,两个字段的数据类型、修饰符不管是否相同,都必须使用不一样的名称,但是对于字节码来讲,如果两个字段的描述符不一致,那字段重名就是合法的
字段计数器fields_count
- fields_count的值表示当前class文件fields表的成员个数,使用两个字节来表示
- fields表中线个成员都是一个field_info结构,用于表示该类或接口所声明的所有类字段或实例字段,不包括方法内部声明的变量,也不包括从父类或父接口继承的那些字段
字段表fields[]
- fields表中的每个成员都必须是一个fields_info结构的数据项,用于表示当前类或接口中某个字段的完整描述
- 一个字段的信息包括如下这些信息,这些信息中,各个修饰符都是布尔值,要么有,要么没有
- 作用域(public,private,protected修饰符)
- 是实例变量还是类变量(static修饰)
- 可变性(final)
- 并发可见性(volatile修饰,是否强制从 内存读写)
- 可否序列化(transient修饰)
- 字段数据类型(基本数据类型、对象、数组)
- 字段名称
- 字段表结构
字段表访问标识
一个字段可以被各种关键字去修饰,如作用域修饰符(public,private,protected)、static修饰符、final修饰、volatile修饰符等,因此,其可像类的访问标志那样,使用一些标志来标记字段,字段的访问标志有如下:
字段名索引
- 根据字段名索引的值,查询常量池中的指定索引项即可
描述符索引
- 描述符的作用用来描述字段的数据类型、方法的参数列表和返回值,根据描述符规则,基本数据类型及代表无返回值的void类型都用一个大写字符来表示,而对象用L加对象的全限定名来表示,如下:
属性表集合
- 一个字段还可能拥有一些属性,用于存储更多的额外信息,比如初始化值、一些注释信息等,属性个数存放在Attribute_count中,属性具体内容存放在attributes数组中
//以常量属性为例
ConstantValue_attribute {u2 attribute_name_index;u4 attribute_length;u2 constantvalue_index;
}
//说明常量属性而言,attribute_length的值恒为2
方法表集合
属性表集合
待续… …