第 16 个常量。这里表示 tag 的值是 07,表示该常量为类信息类型(CONSTANT_Class_info)的常量。从上面的总表查阅知道,该常量项第 2 - 3 个字节表示全限定名常量项的索引,这里是 0017 表示指向常量池第 23 个常量所表示的信息。
第 17 个常量。这里表示 tag 的值是 0C,表示该常量为方法引用类型(CONSTANT_NameAndType_info)的常量。从上面的总表查阅知道,该常量项第 2 - 3 个字节表示字段或方法名的索引,这里是 0018 表示指向常量池第 24 个常量所表示的信息。该常量项的第 4 - 5 个字节表示字段��方法描述符的索引,这里值为 0019 表示指向常量池第 25 个常量所表示的信息。根据我们之前的分析,可以知道第 17 个常量表示的信息其实是:out:Ljava/io/PrintStream;。
第 18 个常量,是一个字符串常量,转换之后是:Hello World。
第 19 个常量。这里表示 tag 的值是 07,表示该常量为类信息类型(CONSTANT_Class_info)的常量。从上面的总表查阅知道,该常量项第 2 - 3 个字节表示全限定名常量项的索引,这里是 001A 表示指向常量池第 26 个常量所表示的信息。
第 20 个常量。这里表示 tag 的值是 0C,表示该常量为方法引用类型(CONSTANT_NameAndType_info)的常量。从上面的总表查阅知道,该常量项第 2 - 3 个字节表示字段或方法名的索引,这里是 001B 表示指向常量池第 27 个常量所表示的信息。该常量项的第 4 - 5 个字节表示字段或方法描述符的索引,这里值为 001C 表示指向常量池第 28 个常量所表示的信息。
第 21 个常量,是一个字符串常量,转换之后是:Demo。
第 22 个常量,是一个字符串常量,转换之后是:java/lang/Object。
第 23 个常量,是一个字符串常量,转换之后是:java/lang/System。
第 24 个常量,是一个字符串常量,转换之后是:out。
第 25 个常量,是一个字符串常量,转换之后是:Ljava/io/PrintStream;。
第 26 个常量,是一个字符串常量,转换之后是:java/io/PrintStream。
第 27 个常量,是一个字符串常量,转换之后是:println。
第 28 个常量,是一个字符串常量,转换之后是:(Ljava/lang/String;)V。
到这里,我们常量池里 28 个常量已经全部解析完了。我们通过手动分析,了解了常量池的构成,但很多时候我们可以借助 JDK 提供的 javap 命令直接查看 Class 文件的常量池信息。
当我们运行javap -verbose Demo.class时,控制台会打印出该 Class 文件的构成信息,其中就包括了常量池的信息。
将利用 javap 打印出的结果,与我们手动分析的结果对比一下,你会发现结果是一致的。
访问标志在常量池结束之后,紧接着的两个字节代表访问标记(access_flags),这个标志用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口、是否定义为public类型、是否定义为abstract类型等。具体的标志位以及标志的含义见下表。
在这里这两个字节是 00 21,通过查看我们并没有发现有标志值是 00 21 的标志名称。这是因为这里的访问标志可能是由多个标志名称组成的,所以字节码文件中的标志值其实是多个值进行或运算的结果。
通过查阅上述表格,我们可以知道,00 21 由 00 01 和 00 20 进行或运算得来。也就是说该类的访问标志是 public 并且允许使用 invokespecial 字节码指令的新语义。
类索引、父类索引、接口索引类索引和父类索引都是一个u2类型的数据,而接口索引集合是一组u2类型的数据的集合,Class文件中由这三项数据来确定这个类的继承关系。
类索引。类索引用于确定这个类的全限定名,它用一个 u2 类型的数据表示。这里的类索引是 00 05 表示其指向了常量池中第 5 个常量,通过我们之前的分析,我们知道第 5 个常量其最终的信息是 Demo 类。
父类索引。父类索引用于确定这个类的父类的全限定名,父类索引用一个u2类型的数据表示。这里的父类索引是 00 06 表示其指向了常量池中第 6 个常量,通过我们之前的分析,我们知道第 6 个常量其最终的信息是 Object 类。因为其并没有继承任何类,所以 Demo 类的父类就是默认的 Object 类。