JAVA類的符號引用的理解


符號引用只是一些符號,包含在字節碼文件的常量池中
它主要包括:
在該類中,出現過的各類包,類,接口,字段,方法等元素的全限定名

有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.非靜態字段
....


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM