符号引用只是一些符号,包含在字节码文件的常量池中 它主要包括: 在该类中,出现过的各类包,类,接口,字段,方法等元素的全限定名
有java类定义如下:
package Clazz;
import java.io.Serializable;
/**
* @Author : ZGQ
* @Date : 2020/3/25 9:56
* @Version : 1.0
*/
public class Rookie extends Person implements Serializable {
int myFiledCommon;
static int myFiledCommonStatic;
final static int myFiledCommonStaticFinal = 10086;
public void method1(){
int a = 1;
int b = 2;
int c = a+b;
}
public void method2(Person person){
System.out.println("THIS person OOPS");
}
private void method3(){
}
}
编译后,经javap工具反编译,常量池内容如下
Constant pool:
#1 = Methodref #6.#32 // Clazz/Person."":()V
#2 = Fieldref #33.#34 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #35 // THIS person OOPS
#4 = Methodref #36.#37 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #38 // Clazz/Rookie
#6 = Class #39 // Clazz/Person
#7 = Class #40 // java/io/Serializable
#8 = Utf8 myFiledCommon
#9 = Utf8 I
#10 = Utf8 myFiledCommonStatic
#11 = Utf8 myFiledCommonStaticFinal
#12 = Utf8 ConstantValue
#13 = Integer 10086
#14 = Utf8
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 LineNumberTable
#18 = Utf8 LocalVariableTable
#19 = Utf8 this
#20 = Utf8 LClazz/Rookie;
#21 = Utf8 method1
#22 = Utf8 a
#23 = Utf8 b
#24 = Utf8 c
#25 = Utf8 method2
#26 = Utf8 (LClazz/Person;)V
#27 = Utf8 person
#28 = Utf8 LClazz/Person;
#29 = Utf8 method3
#30 = Utf8 SourceFile
#31 = Utf8 Rookie.java
#32 = NameAndType #14:#15 // "":()V
#33 = Class #41 // java/lang/System
#34 = NameAndType #42:#43 // out:Ljava/io/PrintStream;
#35 = Utf8 THIS person OOPS
#36 = Class #44 // java/io/PrintStream
#37 = NameAndType #45:#46 // println:(Ljava/lang/String;)V
#38 = Utf8 Clazz/Rookie
#39 = Utf8 Clazz/Person
#40 = Utf8 java/io/Serializable
#41 = Utf8 java/lang/System
#42 = Utf8 out
#43 = Utf8 Ljava/io/PrintStream;
#44 = Utf8 java/io/PrintStream
#45 = Utf8 println
#46 = Utf8 (Ljava/lang/String;)V
反编译结果中,我们可以看到几个典型的符号引用,比如第一项为实例构造器,第四项为该类方法调用的其他方法,第六项为其父类
#1 = Methodref #6.#32 // Clazz/Person."":()V
#4 = Methodref #36.#37 // java/io/PrintStream.println:(Ljava/lang/String;)V
#6 = Class #39 // Clazz/Person
另外,一个类的生命周期中,共有七个阶段,分别是加载,验证,准备,解析,初始化,使用,卸载
其中,我们主要关心解析过程,因为这个过程的主要工作就是,将符号引用转化为直接引用. 解释直接引用之前,我们要先知道 所谓符号引用,只是一个符号而已,只是告知jvm,此类需要哪些调用方法,引用或者继承哪些类等等信息. 但是JVM在使用这些资源的时候,只有这些符号是不行的,必须详细知道这些资源的地址,才能正确地调用相关资源. 直接引用,就是这样一类指针,它直接指向目标. 解析过程,就是完成将符号引用转化为直接引用的过程,方便后续资源的调用.
JAVA中符号引用的出现是非常自然的,因为在类没有加载的时候,也不能确保其调用的资源被加载,更何况还有可能调用自身的方法或者字段. 就算能确保,其调用的资源也不会每次在程序启动时,都加载在同一个地址. 简而言之,在编译阶段,字节码文件根本不知道这些资源在哪,所以根本没办法使用直接引用,于是只能使用符号引用代替.
然而,这只是解析的一部分,因为加载,验证,准备,解析,初始化这五步操作,实际上是类的初始化,换句话说,根本就没有实例对象产生. 所以,以上内容只对类加载时可以确定的符号引用进行解析,比如父类,接口,静态字段,调用的静态方法等 还有一部分,比如方法中的局部变量,实例字段等,他们什么时候开始解析呢.
很可惜,博主也不是全部知道(希望有一天能够划掉)
1.虚方法 和上方解析不同,上方的解析被称为静态解析 在调用虚方法(JVM中的概念)时,JVM只有运行时才知道此方法指向的地址,所以此时必须用到动态连接,具体的实现方法就是分派 但是实际上,分派又分为静态分派和动态分派,静态分派实际上是属于静态解析的,用于方法的重载. 但是遇到方法重写时,便要用到动态分派了,来确定该虚方法的符号应用究竟是指向哪个地址.
2.静态方法中的非静态变量 3.非静态字段 ....