fields_count和fields在class文件中。
fields_count描述了当前类中定义的字段的数量。请注意,它包括静态字段,但不包括从父类继承的字段。如果当前的class文件是由接口生成的,那么这里的fields_count描述了界面中定义的字段,我们知道界面中定义的字段默认是静态的。另外需要注意的是,编译器可能会自动生成字段,也就是说,class文件中的字段中定义的字段多。例如,编译器将为内部类添加一个字段,指向外围类对象。
fields_count下面的数据称为fields,可以看作是数组,数组中的每个项目都是field_info。该数组共有field_countfield_info,每个field_info都是对一个字段的描述。下面我们详细讲解field_info的结构。每个field_info的结构如下:
(1)access_flags。
access_flags占两个字节,描述字段的访问标志信息。这里就不详细介绍了,下面给出一个表格(这个表格来自虚拟机):
(2)name_index。
access_flags下面的两个字节是name_index,指向常量池,描述当前字段的字段名。该索引指向常量池中的constant_utf8_info数据项。存储在这个constant_utf8_info数据项中的字符串是当前字段的字段名。
(3)descriptor_index。
name_index下面的两个字节称为descriptor_index,它也是指向常量池的索引,描述当前字段的描述符。该索引指向常量池中的constant_utf8_info数据项。存储在这个constant_utf8_info数据项中的字符串是当前字段的描述符。
(4)attributes_count和attributes。
descriptor_index以下是attributes_count和attributes。这是对当前字段属性的描述。这里的属性和源文件中的属性不一样。在源文件测量层面,属性是字段的另一个名称。希望读者不要怀疑。读者不应轻视class文件中的属性,这些属性可以描述很多信息。我们将在下面的文章中介绍它。
atttributes_count表示这个字段有几个属性。attributes可以看作是一个数组,数组中的每个项目都是attribute_info,每个attribute_info表示一个属性,数组中有attributes_count个性。filed_info中可以出现三种属性,即constantvalue、deprecated和synthetic。这些属性将在后面的文章中介绍。
我们以代码的形式解释,源代码如下:
package com.bjpowernode.test;
public class programer extends person{
private computer computer;
public programer(computer computer){
this.computer = computer;
}
public void dowork(){
computer.calculate();
}
}
反编译后,常量池中会有以下信息(这里省略了大部分无关信息):
constant pool:
.........
.........
#5 = utf8 computer
#6 = utf8 lcom/jg/zhang/computer;
.........
.........
{
private com.jg.zhang.computer computer;
flags: acc_private
.........
.........
}
从反编译的结果可以看出,源文件定义了一个computer类型的字段computer,它是private。然后常量池中有字段名和描述符。常量池第五项的constant_utf8_info是字段名,第六项的constant_utf8_info是字段的描述符。这里需要注意的是,在反编译programer.class时,由于computer是私有的,需要添加-private选项,否则,虽然常量池中有字段引用信息,但字段信息不会输出,即以下两行不会输出:
private com.bjpowernode.test.computer computer;
flags: acc_private
如果在javap中添加private选项,将有上述两行的输出。使用的命令如下:
javap -c -v -private -
classpath . com.bjpowernode.test.programer
根据反编译的结果,可以给出以下示意图,表明与computer对应的field_info不适合引用常量池(其中常量池在虚线范围内):
methods_count和methods在class文件中。
fields下面的信息是methods_count和methods。methods_count描述了当前类中定义的方法的数量。请注意,它包括静态方法,但不包括从父类继承的方法。如果当前的class文件是由接口生成的,那么这里的methods_count描述了界面中定义的抽象方法的数量,我们知道界面中定义的方法默认是公共的。此外,需要注意的是,编译器在编译过程中可能会向class文件添加额外的方法,即class文件中的方法数量可能超过源文件中用户定义的方法。例如,如果当前类没有定义结构方法,编译器将添加一个无参数的结构函数;如果静态变量定义在当前类或接口中,并使用初始化表达式赋值或定义static静态代码块,编译器将默认添加静态初始化方法。
methods_count下面的数据叫methods,可以看作是数组,数组中的每个项目都是method_info。这个数组有methods_countmethod_info,每个method_info都是对一种方法的描述。下面我们详细讲解method_info的结构。每个method_info的结构如下,与field_info的结构几乎相同:
(1)access_flags。
access_flags占两个字节,描述方法访问标志信息。这里就不详细介绍了,下面给出一个表格(这个表格来自虚拟机):
(2)name_index。
access_flags下的两个字节是name_index,指向常量池,描述当前方法的方法名。该索引指向常量池中的constant_utf8_info数据项。存储在这个constant_utf8_info数据项中的字符串是当前方法的方法名。
(3)descriptor_index。
name_index下面的两个字节称为descriptor_index,它也是指向常量池的索引,描述当前方法的描述符。该索引指向常量池中的constant_utf8_info数据项。存储在这个constant_utf8_info数据项中的字符串是当前方法的描述符。
(4)attributes_count和attributes。
descriptor_index以下是attributes_count和attributes。这是对当前方法所具有的属性的描述。这里的属性和源文件中的属性不一样。在源文件测量层面,属性是字段的另一个名称。希望读者不要怀疑。读者不应轻视class文件中的属性,这些属性可以描述很多信息。我们将在下面的文章中介绍它。
atttributes_count表示这个字段有几个属性。attributes可以看作是一个数组,数组中的每个项目都是attribute_info,每个attribute_info表示一个属性,数组中有attributes_count个性。method_info中可以出现三种属性,即code、deprecated、exceptions和synthetic。在这些属性中,尤其是code和exceptions非常重要。这两个属性在class文件中完整描述一种方法中起着至关重要的作用。code属性中存储方法的字节面指令和exceptions属性是方法声明中抛出的异常描述。这两个属性和其他属性将在下一篇文章中详细介绍。请注意。
介绍了每个method_info的结构后,我们用代码来解释或使用上述源代码:
package com.jg.zhang;
public class programer extends person{
private computer computer;
public programer(computer computer){
this.computer = computer;
}
public void dowork(){
computer.calculate();
}
}
反编译后,常量池中会有以下信息(这里省略了大部分无关信息):
constant pool:
.........
#7 = utf8 <init>
#8 = utf8 (lcom/jg/zhang/computer;)v
.........
#12 = utf8 ()v
.........
#19 = utf8 dowork
{
.........
public com.jg.zhang.programer(com.jg.zhang.computer);
flags: acc_public
.........
public void dowork();
flags: acc_public
.........
}
从反编译结果可以看出,这类方法定义了两种,一种是结构方法,另一种是dowork方法,都是public。这两种方法的描述信息都存储在常量池中。第七项constant_utf8_info是构造方法的方法名,第八项constant_utf8_info是构造方法的方法描述符,第19项constant_utf8_info是dowork方法的方法名,第12项constant_utf8_info是dowork方法的方法描述符。
根据常量池中的信息,可以得到以下示意图,解释了class文件中的method_info是如何引用常量池中的数据项来描述当前类中定义的方法的。图中虚线范围表示常量池所在区域:
总结
到目前为止,我们已经介绍了class文件中的fields和methods。
fields是对当前类中定义的字段的描述,每个字段用field_info表示,fields中有field_count_info。
methods是对当前类别或接口中声明的方法的描述,每种方法用method_info表示,methods中有methods_countmethod_info。