Java Class文件中的常量池

学习JAVA虚拟机一般都看过《深入理解JAVA虚拟机》这本书,这本书的价值这里不做赘述,本文中许多东西也是参考这本书而来。

我们写的程序源码经过javac的编译会转变成class类型的文件,也被称为字节码文件,该文件记录了整个程序或者说当前这个类的所有相关信息,其中有一个很重要的部分被称为常量池。

常量池的概念和存储的内容网上或书里面写的很清楚,本文主要就是聊聊这个常量池里比较有意思的地方。

(本文的阅读前提,已经了解过class文件的存储结构)

首先来看一个非常简单的类:demo.java

public class demo{

    private int a;

    private int b=1;

}

这个类源程序中只申明了两个int类型的变量,其中b赋予了一个初始值1,接着我们通过javac来编译这个文件,编译好了之后,通过javap -verbose demo.class来查看这个class文件。

 

了解class文件存储结构的同学应该都看过类似的图,其中有一项Constant pool就代表了常量池。从#1#17总共17个常量项,所以这个常量池项的数量应该是17+1=18个,对应的字节码文件中的16进制表示应该是0x0012,用WinHex打开这个class文件验证一下:

 

接着我们就来看看这些常量池项都有哪些东西。常量池存储什么东西,在网上或书上都有很明确的定义,主要就是两大类,一大类是字面量,另一大类是符号引用,关于这个符号引用的概念简单一点来讲就是通过一组符号的描述,来准确定位到我们需要访问的目标。

关于符号引用字面理解其实不难,为什么要使用符号引用的原因也很简单,但是网上有一种说法是当我们在类中使用了其他类的字段或方法的时候,因为被使用的方法或字段在javac阶段并没有真正的内存入口地址,所以需要用符号引用来表示,当类被加载到虚拟机之后,在某些阶段会将符号引用转变成直接引用,这种说法准确吗?我们接着往下看:

 

在上图的常量池中,只有一个字段的符号引用,也就是b这个变量,

 

那第一个问题a去哪了?或者说为什么没有a的符号引用?而且ab这两个变量都是demo类自身的变量(不准确的描述,只是为了表达ab这两个变量不是引用的其他类的变量字段)那为什么b字段会在常量池有符号引用呢?有同学会说ab都是实例变量不是类变量,那么我们来修改一下源程序:

public class demo{

    private int a;

    private int b=1;

    private static int c;

}

在修改后的源程序中,我们添加了一个被static修饰的变量c,也就是说这个cdemo类的类变量,接着我们再看看常量池:

 

里面的字段的符号引用依然只有bac都没有,这是为什么呢?

注意:我们这里说的是字段的符号引用没有,a,b,c这个变量本身名字的字面量是存在的,也就是说下图中的a,b,c只是三个utf-8编码的字符窜,并不是字段的符号引用。

 

同样是在本类中定义的三个变量,为何有的会在常量池中有符号引用,有的却没有呢?

接着我们再看方法的符号应用,假如我们随便定义一个方法:

 public class demo{
    private int a;
    private int b=1;
    private static int c;
    public void  haha(){
    }
}

然后看看常量池:

 

方法的引用只有一个java.lang.Object.<init>,并没有我们自己定义的haha()这个方法,有同学会说haha()这个方法不是调用的其他类里面的方法,所以不会有符号引用存储在常量池中,那么我们再来改改:

 public class demo{
    private int a;
    private int b=1;
    private static int c;
    public void  haha(){
    }
    public void hehe(){
        haha();
    }   
}

然后看一下常量池:

 

这时常量池里多了一个方法的符号引用,这里有两个问题可以问:

1. 我们在hehe()这个方法里调用了haha()这个方法,但这两个方法都是定义在demo类本身的,并不是调用了其他类的方法,为什么haha()这个方法会有符号引用?

2. 同样是定义在demo类里的两个方法,为什么haha()有方法的符号引用,而hehe()却没有?

接着我们再定义第三个方法:

 public class demo{
    private int a;
    private int b=1;
    private static int c;
    public void  haha(){
    }
    public void hehe(){
        haha();
    }   
    public static void lala(){
    }
}

常量池:

 

方法的引用依然只有一个,由此可见常量池中的字段的符号引用和方法的符号引用,与字段或方法是类变量还是实例变量并无直接的关联,那么最终的问题是什么样的情况下会在常量池中添加某个字段的符号引用或方法的符号引用呢?以上的所有问题你是否能准确的回答出为什么吗?花点时间先想想,我们接着再聊。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值