常量池的定义
常量池可以理解为class文件之中的资源仓库,它是class文件结构中与其他项目关联最多的数据类型,也是占用class文件空间最大的数据项目之一,同时还是在class文件中第一个出现的表类型数据项目;我们简单理解就是一块缓存区域。
常见的常量池
静态常量池
class字节码文件中的常量池,class文件中的常量池不仅仅包含字符串字面量,还包含类,方法的信息,主要用于编译器存放一些字面量和符号的引用,每个类都有一个class常量池,一般主要存放二大类常量:字面量和符号引用
- 字面量
文本字符串,一些声明为final的常量值 - 符号引用
属于编译原理方面的概念,一般包括下面三类常量
类和接口的全限定名
字段的名称和描述符
方法的名称和描述符
我们首先要了解在class文件中并不会保存方法,字段的内存布局信息,因此这些字段,方法的符号引用不经过运行期转换的话无法得到真正的内存入口地址,当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析,翻译到具体的内存地址之中,这就会涉及到我们常说的运行时常量池。
String s1 = "helloworld";
String s2 = "helloworld";
String s3 = "hello" + "world";
String s4 = "hello" + new String("world");
String s5 = new String("helloworld");
String s6 = s5.intern();
String s7 = "hello";
String s8 = "world";
String s9 = s7 + s8;
System.out.println(s1 == s2); //true
// s1执行值常量池中就会用helloworld,s2的时候不会重新申请空间,把上面的helloworld的内存地址直接给s2;
System.out.println(s1 == s3); //true
// "hello" + "world"在做加号的时候,由于都是字面量,直接会进行优化,将helloworld的值赋给s3
System.out.println(s1 == s4); //false
//一个是常量池里面的字符串,一个是堆里面的,最后会指向堆里面的helloworld,故地址不同
System.out.println(s1 == s9); //false
//这个在编译器无法指定对应的常量池,是二个对象的相加,在通过虚拟机的动态链接之后,指向的是堆里面的一个新的string的对象
System.out.println(s4 == s5); //false,这个是堆里面对应的地址是不相同的
System.out.println(s1 == s6); //true,这个属于String里面的方法,先去判断常量池里面有没有这个字符创常量,没有就放入,有的话返回对应的内存地址就ok了
这里需要了解下局部变量,成员变量的存储位置
一:局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,每个方法都有自己独立的方法栈,
(1)当是基本类型的变量的时,其变量名及值是放在方法栈中
(2)当是引用变量时,所声明的变量是放在方法的栈中,该变量所指向的对象是放在堆类存中的。
二:在类中声明的变量是成员变量,放在堆中的(因为全局变量不会随着某个方法执行结束而销毁)。
(1)当声明的是基本类型的变量其变量名及其值放在堆内存中的
(2)引用类型时,其声明的变量仍然会存储一个内存地址值,该内存地址值指向所引用的对象。引用变量名和对应的对象仍然存储在相应的堆中