目录

相信大家对’字面量’,‘符号引用’,‘直接引用’这些词在书上或者博客上都见了不少. 但是又有多少人真正的理解这几个词?反正我毕业前四年是没有弄懂.

字面量

到底什么是字面量? 这其实要甩锅给前辈们,他们翻译过来之后用了这么个词,虽然比较抽象,但是这词也基本上表达了它的含义。

百度百科是这么解释的

在计算机科学中,字面量(literal)是用于表达源代码中一个固定值的表示法(notation)。几乎所有计算机编程语言都具有对基本值的字面量表示,诸如:整数、浮点数以及字符串;而有很多也对布尔类型和字符类型的值也支持字面量表示;还有一些甚至对枚举类型的元素以及像数组、记录和对象等复合类型的值也支持字面量表示法

相信大家还是不太懂到底什么意思,尤其是字面量和常量又这两个概念.那么举个例子:

static final String MSG = "hello word";
static final int A = 1;
static final float F = 0.52f

那么MSG,A,F就是常说的常量. 而hello word, 1,0.52f就是字面量. 其实就是这么简单. 如你所见的这些个“固定值”就是字面量.

注意在java里面字面量必须是使用final修饰的. 从百度百科中可得知它是一个固定值的表示法.在java里面final修饰表示不变.

符号引用

这里理解了能帮助你更深入的理解类加载

符号引用其实是编译原理的概念,是相对直接引用来说的,所以符号引用和直接引用是有关联的. 简单来说符号引用可以分为三类:

  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符

比如有以下代码:

public class Demo {

    private int b = 3;
    private static Integer a;

    public static void increment() {
        a++;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            increment();
        }
        System.out.println(a);
    }
}

从上面定义来说:

  • 字段的名称a,‘b’和描述符Integer,int是符号引用
  • 方法的名称increment和描述符()是符号引用
  • 类的全限定名{包名}.Demo是符号引用

也可以更直观的看到符号引用,这里需要借助jdk的命令javacjavap;将这个类文件编译一下javac Demo.java得到.class文件,然后再javap -v Demo > demo.txt得到反编译文件,如下:

Classfile xxx/Demo.class
  Last modified 2020-8-21; size 827 bytes
  MD5 checksum a12d6be1ed5686e3aaa0f3f270505693
  Compiled from "Demo.java"
public class com.ai94.user.portrait.Demo
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #10.#28        // java/lang/Object."<init>":()V
   #2 = Fieldref           #9.#29         // com/ai94/user/portrait/Demo.b:I
   #3 = Fieldref           #9.#30         // com/ai94/user/portrait/Demo.a:Ljava/lang/Integer;
   #4 = Methodref          #31.#32        // java/lang/Integer.intValue:()I
   #5 = Methodref          #31.#33        // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   #6 = Methodref          #9.#34         // com/ai94/user/portrait/Demo.increment:()V
   #7 = Fieldref           #35.#36        // java/lang/System.out:Ljava/io/PrintStream;
   #8 = Methodref          #37.#38        // java/io/PrintStream.println:(Ljava/lang/Object;)V
   #9 = Class              #39            // com/ai94/user/portrait/Demo
  #10 = Class              #40            // java/lang/Object
  #11 = Utf8               b
  #12 = Utf8               I
  #13 = Utf8               ConstantValue
  #14 = Integer            3
  #15 = Utf8               a
  #16 = Utf8               Ljava/lang/Integer;
  #17 = Utf8               <init>
  #18 = Utf8               ()V
  #19 = Utf8               Code
  #20 = Utf8               LineNumberTable
  #21 = Utf8               increment
  #22 = Utf8               main
  #23 = Utf8               ([Ljava/lang/String;)V
  #24 = Utf8               StackMapTable
  #25 = Utf8               <clinit>
  #26 = Utf8               SourceFile
  #27 = Utf8               Demo.java
  #28 = NameAndType        #17:#18        // "<init>":()V
  #29 = NameAndType        #11:#12        // b:I
  #30 = NameAndType        #15:#16        // a:Ljava/lang/Integer;
  #31 = Class              #41            // java/lang/Integer
  #32 = NameAndType        #42:#43        // intValue:()I
  #33 = NameAndType        #44:#45        // valueOf:(I)Ljava/lang/Integer;
  #34 = NameAndType        #21:#18        // increment:()V
  #35 = Class              #46            // java/lang/System
  #36 = NameAndType        #47:#48        // out:Ljava/io/PrintStream;
  #37 = Class              #49            // java/io/PrintStream
  #38 = NameAndType        #50:#51        // println:(Ljava/lang/Object;)V
  #39 = Utf8               {包名}/Demo
  #40 = Utf8               java/lang/Object
  #41 = Utf8               java/lang/Integer
  #42 = Utf8               intValue
  #43 = Utf8               ()I
  #44 = Utf8               valueOf
  #45 = Utf8               (I)Ljava/lang/Integer;
  #46 = Utf8               java/lang/System
  #47 = Utf8               out
  #48 = Utf8               Ljava/io/PrintStream;
  #49 = Utf8               java/io/PrintStream
  #50 = Utf8               println
  #51 = Utf8               (Ljava/lang/Object;)V
{
  public com.ai94.user.portrait.Demo();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iconst_3
         6: putfield      #2                  // Field b:I
         9: return
      LineNumberTable:
        line 5: 0
        line 7: 4

  public static void increment();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=0
         0: getstatic     #3                  // Field a:Ljava/lang/Integer;
         3: astore_0
         4: getstatic     #3                  // Field a:Ljava/lang/Integer;
         7: invokevirtual #4                  // Method java/lang/Integer.intValue:()I
        10: iconst_1
        11: iadd
        12: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        15: dup
        16: putstatic     #3                  // Field a:Ljava/lang/Integer;
        19: astore_1
        20: aload_0
        21: pop
        22: return
      LineNumberTable:
        line 11: 0
        line 12: 22

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: iconst_0
         1: istore_1
         2: iload_1
         3: bipush        100
         5: if_icmpge     17
         8: invokestatic  #6                  // Method increment:()V
        11: iinc          1, 1
        14: goto          2
        17: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        20: getstatic     #3                  // Field a:Ljava/lang/Integer;
        23: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
        26: return
      LineNumberTable:
        line 15: 0
        line 16: 8
        line 15: 11
        line 18: 17
        line 19: 26
      StackMapTable: number_of_entries = 2
        frame_type = 252 /* append */
          offset_delta = 2
          locals = [ int ]
        frame_type = 250 /* chop */
          offset_delta = 14

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: iconst_2
         1: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         4: putstatic     #3                  // Field a:Ljava/lang/Integer;
         7: return
      LineNumberTable:
        line 8: 0
}
SourceFile: "Demo.java"

我们关注重点常量池(Constant pool)部分:

现在所看到的常量池也叫静态常量池,因为它现在还没运行,是静态存储在.class文件中的.而相对它而言的运行时常量池 这个概念即是将.class文件加载到内存中运行时这个常量池就叫运行时常量池

  #11 = Utf8               b
  #12 = Utf8               I
  #13 = Utf8               ConstantValue
  #14 = Integer            3
  #15 = Utf8               a
  #16 = Utf8               Ljava/lang/Integer;
  #17 = Utf8               <init>
  #18 = Utf8               ()V
  #19 = Utf8               Code
  #20 = Utf8               LineNumberTable
  #21 = Utf8               increment
  #39 = Utf8               {包名}/Demo

这些抽取出来的关键信息就是字面量3,以及符号引用b,a. 所以常量池中包含符号引用和字面量。

字节码文件中#12 I表示Int

直接引用

从上面得知符号引用其实是静态的,那么直接引用就是动态的. 将.class文件加载到内存中之后,jvm会将符号引用转化为代码在内存中实际的内存地址.那么这就是直接引用.也就是类加载中的动态链接