直接引用
直接引用和虚拟机的布局是相关的,不同的虚拟机对于相同的符号引用所翻译出来的直接引用一般是不同的。如果有了直接引用,那么直接引用的目标一定被加载到了内存中。 直接引用可以是:
- 直接指向目标的指针。(个人理解为:指向对象,类变量和类方法的指针)
- 相对偏移量。(指向实例的变量,方法的指针)
- 一个间接定位到对象的句柄。
符号引用
符号引用则属于编译原理方面的概念,包括了下面三类常量:
- 类和接口的全限定名(Fully Qualified Name)
- 字段的名称和描述符(Descriptor)
- 方法的名称和描述符
符号引用存在原因:
Java代码在进行Javac编译的时候,并不像C和C++那样有“连接”这一步骤,而是在虚拟机加载Class文件的时候进行动态连接。也就是说,在Class文件中不会保存各个方法、字段的最终内存布局信息,因此这些字段、方法的符号引用不经过运行期转换的话无法得到真正的内存入口地址,也就无法直接被虚拟机使用。当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址之中。
类的全限定名
类的全限定名称很好理解,比如Object类他的包名为java.lang,所以他的全限定名就是java/lang/Object,他仅仅是把类中的“.”替换为“/”而已,为了让多个全限定名不会混乱,一般会在结尾添加";"表示结束。
简单名称
简单名称就是指没有参数修饰和类型的方法或者字段名,比如clone()方法的简单名称就是clone,字段a的简单名称就是m;
描述符
相对于全限定名和简单名称来说,方法和字段的描述符就要复杂一些。描述符的作用是 用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。根据描述符规则,基本数据类型(byte、char、double、float、int、long、short、boolean)以及代表 无返回值的void类型都用一个大写字符来表示,而对象类型则用字符L加对象的全限定名来表示。
对于数组类型,每一个维度将使用一个前置的“[”,二维数组则使用两个“[, 如一个定义为 ”java.lang.String[][]“的二维类型的数组,将被记录为:“[[Ljava/lang/String;”,一个整型数 组“int[]”将被记录为“[I”。
描述符描述方法的时候,按照先参数列表,后返回值的顺序描述,参数列表按照参数的严格顺序放在一组小括号“()”之内。如方法void inc()的描述符为“()V”,方法 java.lang.String toString()的描述符为“()Ljava/lang/String;”,方法int indexOf(char[]source,int sourceOffset,int sourceCount,char[]target,int targetOffset,int targetCount,int fromIndex)的描述符为“([CII[CIII)I”。