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.非静态字段
....

posted @ 2020-03-08 16:50  断腿三郎  阅读(4081)  评论(0编辑  收藏  举报