解析一: 符号引用就是一个类中(当然不仅是类,还包括类的其他部分,比如方法,字段等),引入了其他的类,可是 JVM 并不知道引入的其他类在哪里,所以就用唯一符号来代替,等到[类加载]器去解析的时候,就把符号引用找到那个引用类的地址,这个地址也就是直接引用。
解析二:
- 符号引用(Symbolic References):
符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。例如,在 Class 文件中它以 CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info 等类型的常量出现。符号引用与虚拟机的内存布局无关,引用的目标并不一定加载到内存中。在 Java 中,一个 java 类将会编译成一个 class 文件。在编译时,java 类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。比如 org.simple.People 类引用了 org.simple.Language 类,在编译时 People 类并不知道 Language 类的实际内存地址,因此只能使用符号 org.simple.Language(假设是这个,当然实际中是由类似于 CONSTANT_Class_info 的常量来表示的)来表示 Language 类的地址。各种虚拟机实现的内存布局可能有所不同,但是它们能接受的符号引用都是一致的,因为符号引用的字面量形式明确定义在 Java 虚拟机规范的 Class 文件格式中。
- 直接引用: 直接引用可以是 (1)直接指向目标的指针(比如,指向 “类型”【Class 对象】、类变量、类方法的直接引用可能是指向方法区的指针) (2)相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量) (3)一个能间接定位到目标的句柄 直接引用是和虚拟机的布局相关的,同一个符号引用在不同的虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经被加载入内存中了。
解析三: 符号引用即用 (用字符串符号的形式) 来表示引用,其实被引用的类、方法或者变量还没有被加载到内存中。而直接引用则是有具体引用地址的指针,被引用的类、方法或者变量已经被加载到内存中。以变量举个例子:
符号引用要转换成直接引用才有效,这也说明直接引用的效率要比符号引用高。那为什么要用符号引用呢?这是因为类加载之前,javac 会将源代码编译成. class 文件,这个时候 javac 是不知道被编译的类中所引用的类、方法或者变量他们的引用地址在哪里,所以只能用符号引用来表示,当然,符号引用是要遵循 java 虚拟机规范的。还有一种情况需要用符号引用,就例如前文举得变量的符号引用的例子,是为了逻辑清晰和代码的可读性。 javac 在编译一个 A 类时, 如果 A 类应用了 B 类, 那么 javac 到类路径下找 B 类, 如果找到了, 就把 A 类编译成 A.class 文件, 这时 A.class 文件中有个 B 类的符号引用 (字符串形式), 这个可以自己写两个 A 类和 B 类, A 类引用 B 类, 编译 A 类后, 使用 JDK 自带反编译工具, 反编译出代码看
我们都知道,类加载过程分为加载—> 验证—> 准备—> 解析—> 初始化这 5 个阶段,符号引用转换为直接引用就发生在解析阶段,解析阶段可能在初始化前,也可能在初始化之后。