字面量,符号引用,直接引用究竟是什么?
目录
相信大家对’字面量’,‘符号引用’,‘直接引用’这些词在书上或者博客上都见了不少. 但是又有多少人真正的理解这几个词?反正我毕业前四年是没有弄懂.
字面量
到底什么是字面量? 这其实要甩锅给前辈们,他们翻译过来之后用了这么个词,虽然比较抽象,但是这词也基本上表达了它的含义。
百度百科是这么解释的
在计算机科学中,字面量(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的命令javac
和javap
;将这个类文件编译一下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会将符号引用转化为代码在内存中实际的内存地址.那么这就是直接引用.也就是类加载中的动态链接
。