6.3 特殊字符串
常量池中容纳的符号引用包括三种特殊的字符串:全限定名、简单名称和描述符。所有的符 号引用都包括类或者接口的全限定名。字段的符号引用除了全限定类型名之外,还包括简单字段 名和字段描述符。方法的符号引用除了全限定类型名之外,还包括简单方法名和方法描述符。
在符号引用中使用的特殊字符串也同样用来描述被class文件定义的类或者接口。例如,定义过的类或者接口会有一个全限定名。对于每一个在类或者接口中声明的字段,常量池屮都会 有一个简单名称和字段描述符。对于每一个在类或者接口中声明的方法,常量池中都会有一个 简单名称和方法描述符。
6.3.1全限定名
当常最池入口指向类或者接口时,它们给出该类或者接口的全限定名。在class文件中,全限定名中的点用斜线取代了。例如,在class文件中,java.lang.Object的全限定名表示为 java/lang/Object;在class文件中,java.util.Hashtable的全限定名表示为java/util/Hashtable。
6.3.2简单名称
字段名和方法名以简单名称(非全限定名)形式出现在常量池人口中。例如,一个指向类 java.lang.Object所属方法String toString ()的常量池入口有一个形如“toString”的方法名。一 个指向类java.lang.System所属字段java.io.PrintStream out的常量池人口有一个形如“out“的字段名。
6.3.3描述符
除了类(或接口)的全限定名和简单字段(或方法)名,指向字段和方法的符号引用还包 含描述符字符串。字段的描述符给出了字段的类型;方法描述符给出了方法的返回值和方法参 数的数量、类型以及顺序。
字段和方法的描述符由如下所示的上下文无关语法定义。该语法中非终结符号用斜体字标
6.4常量池
常量池是一个可变长度cp_info表的有序序列。这些cp_info表的通常形式如表6-8所示。cp_info表中的tag (标志)项是一个无符号的byte类型值,它表明了表的类型和格式。cp_info表 一共有11种类型,这些类型将在下面的小节中一一进行阐述。
表6-8 cp_info表的通常形式
类型 名称 数量
u1 tag 1
u1 info 根据tag值决定
6.4.1 CONSTANT_Utf8_info 表
可变长度的CONSTANT_Utf8_info表使用一种UTF-8格式的变体来存储一个常量字符串。这 种类型的表可以存储多种字符串,包括:
如下文所示:CONSTANT_Utf8_info表中存储了四种基本信息类型:文字字符串、被定义的类和接口描述、对其他类或接口的符号引用以及与属性相关的字符串。一些与属性相关的字符串如:属性名称、产生class文件的源文件名称、局部变量的名称以及描述符。
UTF-8编码模式允许字符串中的所有Unicode字符以2个字节的形式表示,而ASC1l字符(空字符null除外〉以一个字节的形式表示。表6-9列出了CONSTANT_Utf8_info的格式。
表6-9 CONSTANT_Utf8_info表的格式
类 型 名 称 数 IS
ul tag 1
u2 length 1
ul bytes length
bytes
bytes项中包含按照变体UTF-8格式存储的宇符串中的字符。从‘\u0001’到‘\u007f’的所有字符(除空字符null外所有的ASCII宇符)都使用一个字节表示。
空字符null ( ‘\uOOOO’ )和从‘\u0080',到'\u07ff'的所有字符使用两个字节表示:
从‘\u0800’到‘\uffff’的所有字符使用三个字节表示。
在CONSTANT_Utf8_info表内。bytes项中的UTF-8字符串编码与标准UTF-8格式的区别在于: 第一,在标准UTF-8编码模式中,空字符null使用一个字节表示;在CONSTANT_Utf8_info表中, 空字符使用两个字节表示。这种对于空字符null的双字节编码,意味着bytes项的值永远不会为0。第二,bytes项中只使用了标准UTF-8编码屮的单字节、双字节和三字节编码,而标准UTF-8编码 还包括未在CONSTANT_Utf8_info表屮使用的较长的格式。
6.4.6 CONSTANT_Class_info表
固定长度的C0NSTANT_class_info表使用符号引用来表述类或者接口。无论指向类、接口、 字段、还是方法,所有的符号引用都包含一个CONSTANT_Class_info表。表6-4列出了 CONSTANT_Class_info 表的格式。
表6-14 CONSTANT_Class_info表的格式
类型 名称 数量
u1 tag 1
u2 name_index I
name_index项给出了包含类或者接口全限定名的CONSTANT_Utf8_info表的索引。
表6-16 CONSTANT_Field_info表的格式
类型 名称 数量
U1 tag I
u2 class_index I
u2 name_and_type_index 1
class_index项给出了声明被引用字段的类或者接口的CONSTAT_Class_info人口的索引。
需要注意的是,由class_index指定的CONSTAT_Class_info不只是代表类,还可能代表接 口。尽管接口中能够声明字段,而且可以分别声明为公开、静态和final类型。但如前所述,如 果其他类的静态final字段使用编译时的常量进行初始化操作,那么class文件不包含对这些字段 的符号引用。但是,class文件可以包含它使用的任何这些静态final字段的常量值的复本。例如, 如果类使用在接口中声明的float类型的静态final字段,而且它被初始化为编译时的常量,该类 将会在它自己的存储float值的常量池中拥有一个CONSTAT_Float_info表。但是如果该接口使 用只有在运行时才能计算出的表达式来初始化它的静态final字段,那么在使用该字段的类的常 量池中,将会有一个对该接口中的字段进行符号引用的CONSTANT_Fieldref_info表。
6.4.9 CONSTANT_Methodref_info表
固定长度的CONSTANT_Methodref_info表使用符号引用来表述类中声明的方法(不包括接 口中的方法)。表6-17列出了CONSTANT_Methodref_info表的格式。
表6-17 CONSTANT_Methodref_info表的格式
类型 名称 数量
u1 tag 1
u2 class_index 1
u2 name_and_type_index 1
class_index项给出了声明了被引用方法的类的C0NSTANT_Class_info入口的索引。由 class_index所指定的C0NSTANT_Class_info表必须为类,不能为接口。指向接口中声明的方法的符号引用使用 CONSTANT_interfaceMethodref表。
name_and_type_index提供了C0NSTANT_NameAndType_info入口的索引,该人口提供了方 法的简单名称以及描述符。如果方法的简单名称开始于“<”(‘\U003c’)符号,该方法必须为 —个实例初始化方法。它的简单名称必须为<init>,它的返回值必须为void类型。否则,该方法
6.4.11 CONSTANT_NameAndType_info 表
固定长度的CONSTANT_NameAndType_info表构成指向字段或者方法的符号引用的一部分。 该表提供了所引用字段或者方法的简单名称和描述符的常量池入口。
表6-19列出了CONSTANT_NameAndType_info 表的格式。
表6-19 CONSTANT_NameAndType_info 表的格式
类型 名称 数量
u1 tag 1
u2 name_index 1
u2 descriptor_index 1
6.5字段
在类或者接口屮声明的每一个字段(类变量或者实例变量)都由class文件中的一个名为 field_info的可变长度的表进行描述。在一个class文件中,不会存在两个具有相同名字和描述的字段。(需要注意的是,尽管在Java程序设计语言中不会有两个具有相同名字的字段存在于同一 个类或者接口中,但一个class文件中的两个字段可以拥有同一个名字一只要它们的描述符不 同。换句话说,尽管在java程序设计语言中无法在同一个类或者接口中定义两个具有同样名字和 不同类别的字段,但是两个这样的字段却可以同时合法地出现在一个Java class文件中。)表6-20 中列出了 field_info表的格式。
表6-20 field_info表的格式
类型 名称 数董
u2 access_flags 1
u2 namc_index 1
u2 descriptor_index 1
u2 attributes_count 1
attribute_info attributes attributes_count
attributes项是由多个attribute_info表组成的列表。attributes_count指出列表中 attribute_info表 的数量。一个字段在其列表中可以有任意数量的属性。由Java虚拟机规范定义的三种可能会出现 在此项中的属性是:ConstantValue、Deprecated和Synthetic。这三种属性将在本章后面进一步阐 述。java虚拟机惟一需要识别的属性是ConstantValue属性。虚拟机实现必须忽略任何无法识别的 属性。
自我感言:
field_info和CONSTANT_Field_info的区别:
field_info是某类自身的field的内部表示信息,而CONSTANT_Field_info是该类的字段的外部表示,是用来被引用的(包括本类和其他类),例如某类的属性表中会引用到其他类的CONSTANT_XXX_info,比如某个方法的异常表属性引用异常类对应的CONSTANT_class_info。常量池是全局公共的,是被所有类共享的。
--------------------------
6.6方法
在class文件中,每个在类和接口中声明的方法,或者由编译器产生的方法,都由一个可变 长度的method_info表来描述。同一个类中不能存在两个名字及描述符完全相同的方法。需要注 意的是,在Java程序设计语言中,尽管在同一个类或者接口中声明的两个方法不能有同样的特征 签名(除返回类型之外的描述符),但在同一个class文件中,两个方法可以拥有同样的特征签名, 前提是它们的返回值不能相同。换句话说:在java源文件的同一个类里,如果声明了两个具有相 同名字和相同参数类型,但返回值不同的方法,这个程序将无法编译通过。在java程序设计语言 中,不能仅仅通过返回值的不同来重载方法。但是这样的两个方法可以和谐地在一个class文件 中共存。
有可能在class文件中出现的两种编译器产生的方法是:实例初始化方法(名为<init>)和类 与接口初始化方法(名为<clinit> )。需要了解更多有关编译器产生的方法,请参照第7章。 method_info表的格式如表6-22所示。
表6-22 method_info表的格式
类 型 名 称 数量
u2 access_flags 1
u2 name_index 1
u2 descriptor_index 1
u2 attributes_count 1
attribute_info attributes attribuies_count
access_flags
在声明方法时使用的修饰符存放在方法的access_flags项中。表6-23列出了各个标志所使用 的位。1.2版本中加进了ACC_STRICT标志,它指明方法中的所有表达式都必须使用FP_strict模 式进行计算。第14章详细阐述了FP-strict模式。
类(不包括接口)中声明的方法只能拥有ACC_PUBLIC、ACC_PRIVATE、 ACC_PROTECTED这三个标志中的一个。如果设定了一个方法的ACC_ABSTRACT标志,那么它的ACC_PRIVATE、ACC_STATIC, ACC_FINAL、ACC_SYNCHRONIZED, ACC_NATIVE 以及ACC_STRICT标志都必须清除。接口中声明的所有方法必须有ACC_PUBLIC和 ACC_ABSTRACT标志,除此以外接口方法不能使用其他标志,但接口初始化方法(<clinit> ) 可以使用ACC_STRICT标志。
实例初始化方法(<init>)可以只使用ACC_PUBUC、ACC_PRIVATE和ACC_PROTECTED 标志。因为类与接口初始化方法(<clinit>)只由Java虚拟机直接调用,永远不会被Java字节码 直接调用,这样,<clinit>方法的access_flags中的标志位,除去ACC_STRICT之外的所有位都应 该被忽略。
name_index项提供了CONSTANT_Utf8_info人口的索引,该入口给出了方法的简单名称(不是全限定名)。在class文件中的每一个方法的名称,都必须或者为<init>,或者为<clinit>,或者 是Java程序设计语言中有效的方法名称(简单名称,不是全限定名)。
attributes项是由多个attribute_info表组成的列表。attributes_count给出列表中attribute_info表 的数量。一个字段在其列表中可以有任意数量的属性。在此项中可能会出现的由java虚拟机规范 定义的四种属性是:Code、Deprecated, Exceptions和Synthetic。这四种属性将在本章后面进一步 阐述。Java虚拟机只需要识别Code和Exception属性。虚拟机实现必须忽略任何无法识别的属性。