什么是“字面量”和“符号引用”和"直接引用"
最近看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.class
Last modified 2019-9-19; size 448 bytes
MD5 checksum 2dd9130ca78cd6781198832576a02832
Compiled from "A.java"
public interface com.xiaoer.A
minor version: 0
major version: 54
flags: 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: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #1 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #2 // String defaultB
5: invokevirtual #3 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 5: 0
line 6: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcom/xiaoer/A;
public static void staticA();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #1 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String staticA
5: invokevirtual #3 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 8: 0
line 9: 8
}
2.运行时常量池(方法区内存中,元空间)
在jvm在执行某个类的时候,必须经过加载、链接(验证、准备、解析)、初始化,
在加载阶段:将class文件常量池加载到运行时常量池的过程,这里需要强调一下不同的类共用一个运行时常量池,同时在进入运行时常量池的过程中,多个class文件中常量池中相同的字符串只会存在一份在运行时常量池,这也是一种优化。
运行时常量池相对于class常量池一大特征就是具有动态性,java规范并不要求常量只能在运行时才产生,也就是说运行时常量池的内容并不全部来自class常量池,在运行时可以通过代码生成常量并将其放入运行时常量池中,这种特性被用的最多的就是String.intern()。