什么是“字面量”和“符号引用”和"直接引用"
最近看jvm时遇到了“字面量”和“符号引用”这两个概念,它们被存放在运行时常量池,看了一些博客以后对这两个概念有了初步认识。
字面量可以理解为实际值,int a = 8中的8和String a = "hello"中的hello都是字面量
符号引用就是一个字符串,只要我们在代码中引用了一个非字面量的东西,不管它是变量还是常量,它都只是由一个字符串定义的符号,这个字符串存在Class文件常量池里,类加载的时候第一次加载到这个符号时,就会将这个符号引用(字符串)解析成直接引用(指针)
符号引用主要包括三种常量:
1.类和接口的全限定名
2.字段的名称和描述符
3.方法的名称和描述符
直接引用可以是:
1.直接指向目标的指针。(个人理解为:指向对象,类变量和类方法的指针)
2.相对偏移量。 (指向实例的变量,方法的指针)
3.一个间接定位到对象的句柄。
java的变量有哪些
什么是常量
定义一个变量,用final去修饰,这个变量被赋值一次就不可被改变了, 就成了常量
分为三种:
1).静态常量: final修静态变量
2).实例常量: final修实例变量
3).局部常量: final修局部变量
常量池存什么
1).常量池包含字符串常量池,存的是字符串常量.
2).常量池包含五种基本类型的包装类.即Byte,Short,Integer,Long,Character,Boolean,
这5种包装类默认创建了数值[-128,127]的相应类型的缓存数据,但是超出此范围仍然会去创建新的对象。
而两种浮点数类型的包装类Float,Double并没有实现常量池技术
3).被final修饰的变量, 它们是静态常量\实例常量\局部常量
什么是常量池?
0.什么是常量池
常量池的本质是缓存。不同的类共用一个运行时常量池。
常量池分为:
- Class 文件常量池(非运行时常量池,本地文件)
- 运行时常量池(方法区内存中,元空间)
- 字符串常量池(堆内存)
1.Class 文件常量池(非运行时常量池,本地文件)
Class常量池中存放的编译期生成的各种字面量和符号引用。这部分内容将在类加载后进入方法区的运行时常量池中存放。
接口A源码
package com.xiaoer;public interface A {default void defaultB() {System.out.println("defaultB");}static void staticA() {System.out.println("staticA");}
}
执行javap -verbose ByteCode.class
查看class文件结构。
Classfile /E:/work_space/04 study/test01/target/classes/com/xiaoer/A.classLast modified 2019-9-19; size 448 bytesMD5 checksum 2dd9130ca78cd6781198832576a02832Compiled from "A.java"
public interface com.xiaoer.Aminor version: 0major version: 54flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
Constant pool:#1 = Fieldref #17.#18 // java/lang/System.out:Ljava/io/PrintStream;#2 = String #7 // defaultB#3 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V#4 = String #14 // staticA#5 = Class #21 // com/xiaoer/A#6 = Class #22 // java/lang/Object#7 = Utf8 defaultB#8 = Utf8 ()V#9 = Utf8 Code#10 = Utf8 LineNumberTable#11 = Utf8 LocalVariableTable#12 = Utf8 this#13 = Utf8 Lcom/xiaoer/A;#14 = Utf8 staticA#15 = Utf8 SourceFile#16 = Utf8 A.java#17 = Class #23 // java/lang/System#18 = NameAndType #24:#25 // out:Ljava/io/PrintStream;#19 = Class #26 // java/io/PrintStream#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V#21 = Utf8 com/xiaoer/A#22 = Utf8 java/lang/Object#23 = Utf8 java/lang/System#24 = Utf8 out#25 = Utf8 Ljava/io/PrintStream;#26 = Utf8 java/io/PrintStream#27 = Utf8 println#28 = Utf8 (Ljava/lang/String;)V
{public void defaultB();descriptor: ()Vflags: ACC_PUBLICCode:stack=2, locals=1, args_size=10: getstatic #1 // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc #2 // String defaultB5: invokevirtual #3 // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnLineNumberTable:line 5: 0line 6: 8LocalVariableTable:Start Length Slot Name Signature0 9 0 this Lcom/xiaoer/A;public static void staticA();descriptor: ()Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=0, args_size=00: getstatic #1 // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc #4 // String staticA5: invokevirtual #3 // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnLineNumberTable:line 8: 0line 9: 8
}
2.运行时常量池(方法区内存中,元空间)
在jvm在执行某个类的时候,必须经过加载、链接(验证、准备、解析)、初始化,
在加载阶段:将class文件常量池加载到运行时常量池的过程,这里需要强调一下不同的类共用一个运行时常量池,同时在进入运行时常量池的过程中,多个class文件中常量池中相同的字符串只会存在一份在运行时常量池,这也是一种优化。
运行时常量池相对于class常量池一大特征就是具有动态性,java规范并不要求常量只能在运行时才产生,也就是说运行时常量池的内容并不全部来自class常量池,在运行时可以通过代码生成常量并将其放入运行时常量池中,这种特性被用的最多的就是String.intern()。