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

  • 相关阅读:
    问世即屠榜的bert
    写给日后面试的小朋友们~
    SQL笔记续补
    《姜子牙》视频笔记
    知识图谱之小米的落地与应用探索
    Pyspark ml
    一个小时学会用 Go 编写命令行工具
    C#设计模式-组合模式(Composite Pattern)
    C#设计模式-桥接模式(Bridge Pattern)
    C#设计模式-装饰器模式(Decorator Pattern)
  • 原文地址:https://www.cnblogs.com/ZGQblogs/p/12443358.html
Copyright © 2020-2023  润新知