Class文件常量池
上一篇博客简单的介绍了一下class文件的格式。不清楚的小伙伴,请看一下上篇内容。本篇主要对class文件中的常量池进行重点的分析和介绍。
魔数后面分别是次版本号和主版本号。由上图可知其分别占用两个字节。
接下来就是说明常量的个数了。代表着常量池中有多少个常量,由于常量池中的常量数量不确定,所以才会有这个数据项。依然看上图可知该数据项是占用2个字节,因此顺着主版本号往后面数两个字节得到:0x002E(16进制),即十进制的51,也就是说常量池中有50项常量,索引从1到50。这里所指的常量与JAVA代码中所说的常量有所不同,这里的常量主要包括字面量和符号引用,这两个概念很好理解。
字面量跟JAVA代码中的常量概念类似,如字符串、常量的值等等。
符号引用指的是类与接口的全限定名、字段、方法的名词和描述符。可以暂时理解为类、接口、字段、方法的名字。这里我们来回忆一下类加载机制中的解析阶段:他是将符号引用转化为直接引用。直接引用指的就是可以直接指向目标的指针。可以粗略的理解为:符号引用只是用一些符号来描述他要引用的目标,而直接引用才是真正的指向了他要引用的目标。
在常量池中的每个数据项都是以表的形式存在的,这里每个表都会有一个标志位tag,来说明自己的是哪一类型的数据。如下:
类型 | 名称 | 数量 | 说明 |
---|---|---|---|
u4 | magic | 1 | 魔数:确定一个文件是否是Class文件 |
u2 | minor_version | 1 | Class文件的次版本号 |
u2 | major_version | 1 | Class文件的主版本号:一个JVM实例只能支持特定范围内版本号的Class文件(可以向下兼容)。 |
u2 | constant_pool_count | 1 | 常量表数量 |
cp_info | constant_pool | constant_pool_count-1 | 常量池:以理解为Class文件的资源仓库,后面的其他数据项可以引用常量池内容。 |
u2 | access_flags | 1 | 类的访问标志信息:用于表示这个类或者接口的访问权限及基础属性。 |
u2 | this_class | 1 | 指向当前类的常量索引:用来确定这个类的的全限定名。 |
u2 | super_class | 1 | 指向父类的常量的索引:用来确定这个类的父类的全限定名。 |
u2 | interfaces_count | 1 | 接口的数量 |
u2 | interfaces | interfaces_count | 指向接口的常量索引:用来描述这个类实现了哪些接口。 |
u2 | fields_count | 1 | 字段表数量 |
field_info | fields | fields_count | 字段表集合:描述当前类或接口声明的所有字段。 |
u2 | methods_count | 1 | 方法表数量 |
method_info | methods | methods_count | 方法表集合:只描述当前类或接口中声明的方法,不包括从父类或父接口继承的方法。 |
u2 | attributes_count | 1 | 属性表数量 |
attributes_info | attributes | attributes_count | 属性表集合:用于描述某些场景专有的信息,如字节码的指令信息等等。 |
下面是常量池各个tag,如下图所示:
执行如下代码:
package test07;
/**
* 常量池中各部分内容的例程
* @Package test07
* @Title: Demo.java
* @Company: $
* @author BurgessLee
* @date 2018年9月4日-下午9:27:24
* @Description: $
*/
public class Demo {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) {
Demo d = new Demo();
d.setName("BurgessLee");
System.out.println(d.getAge());
}
}
紧接着常量池数量之后的便是常量表了。刚刚也说了,每个表都会有一个一个字节的标志位,那么常量池数量0x002E之后一个字节便是0x0A,这个就是标志位,十进制是10,查表可知是个方法的符号引用。 他的表结构如下:
tag:
字段为9,方法为10,接口方法为11(从上面表格中可以看到);
class_index:
是对CONSTANT_Class _info类型常量的一个有效索引,代表一个类或接口,而当前字段或方法是这个类或接口的成员;
name_and_type_index:
是对CONSTANT_NameAndType_info类型常量的一个有效索引,代表当前字段或方法的名字和描述符(名字和描述符定义看前面)。
更多其他常量池的信息,网上很多博客已经介绍很全面了。此处便不再进行更多的阐述。
针对JVM相关的内容,就先到这里了。虽然不是完全的独创,但是也包含有自己的理解与注释等内容。如果不对的地方,还请指正,更多内容,请关注我的博客,我们下篇再续。