符號引用只是一些符號,包含在字節碼文件的常量池中
它主要包括:
在該類中,出現過的各類包,類,接口,字段,方法等元素的全限定名
有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."<init>":()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 <init>
#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 // "<init>":()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."<init>":()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.非靜態字段
....
