目录
1 Class常量池
2 运行时常量池
3 字符串常量池
3.1 为什么要设计字符串常量池
3.2 字符串对象三种创建姿势
3.3 字符串的+操作
3.4 字符串的不可变性
4 包装类型常量池
1 Class常量池
- class 文件的资源仓库
- javap命令可以查看class常量池
- 主要包含字面量和符号引用
- 字面量
- 由数字,字母,特殊符号等组成的常量值
- 通常包含字符串字面量和数值字面量
- 字面量包含代码中出现的常量值,类或者接口的全限定名,属性的名称和描述符,方法的名称和描述符
- 符号引用
- 指的是类或者接口的全限定名,属性的名称和描述符,方法的名称和描述符等在常量池中以引用形式存在,其指向常量池中一个或者多个字面量的索引值
- 每一个符号引都有其特殊含义,表明是类,接口,方法,或者属性
- 每一个符号引用都会指向一个或者多个具体的字面量
2 运行时常量池
- Class常量池在jvm运行时被load进内存中
- 其中符号引用被替换为直接引用,即其指向字面量的内存地址
3 字符串常量池
3.1 为什么要设计字符串常量池
- 字符串作为基础的数据类型,会被很频繁的使用
- 每次创建字符串对象,需要对其分配内存,这个是非常消耗时间和内存空间的操作,极大的影响程序的性能
- jvm为了提升性能和减少内存开销,在字符串实例化的时候进行了一些优化
- 为字符串开辟字符串常量区
- 创建字符串常量时,首先查询字符串常量池是否存在该字符串
- 存在时,则返回引用;不存在时实例化改字符串并放入常量池中
- 字符串常量池存储在堆中
3.2 创建字符串对象的三种姿势
- 字符串对象内存分配涉及对象本身以及其内部char数组对象内存分配
- 只要字符串值相同的对象,均指向同一个char数组对象
- 至于字符串对象指向引用是否相同,则需要看具体操作
- 直接赋值创建
String str = "test";
String intern = str.intern();
-
- 在字符串常量池中查看test是否存在
- 如果存在,则返回引用
- 如果不存在,则在字符串常量池中创建一个对象再返回引用
- 此时intern()方法返回引用和str是同一个引用
- new String("test")创建
String str = new String("test");
String intern = str.intern();
-
- new String操作执行之前,因为test通过形参传入,会先执行直接赋值流程
- 然后new String时,会给该对象分配内存,
- 但是其内部value属性会在常量池查找,找到test的char数组对象指向该引用
- 此时调用intern()方法返回字符串常量池引用,与str引用不同
- new String(bytes)创建
byte[] bytes = new byte[]{116,101,115,116};
String str = new String(bytes);
String intern = str.intern();
-
- new String时直接创建字符串对象
- 调用intern()方法时,在字符串常量池分配内存,内存值指向str对象
- 因此intern()方法返回的引用与str引用一致
- 未调用intern()方法时
-
- 调用intern()方法时
-
- 在调用intern()方法时前执行直接赋值操作
3.3 字符串的+操作
- 当+号两边都是常量时,编译时确定,等同与没有+号
String a = "ab";
String b = "a"+"b";System.out.println(a == b); //true,都为常量池引用
- 当+号的一边存在变量时,使用StringBuilder的append拼接字符串
String a = "ab";
String b = "b";
String c = "a"+ b;
//false,a为常量池引用,
//c为StringBuilder对象toString()返回对象,new的新对象
System.out.println(a == c);
- 当+号的一边存在变量时,但是+号使用final修饰,编译时确定
String a = "ab";
final String b = "b";
String c = "a"+ b;
System.out.println(a == c); //true,a,c均指向常量池引用
3.4 字符串的不可变性
- 字符的重新赋值操作是创建新的字符串对象,之前的字符串对象如果没有其他引用指向,会被垃圾回收器回收
- 字符串的+操作,在存在变量的情况下,都是采用StringBuilder对象拼接子串,最后调用toString()方法返回新的字符串对象
- 字符串对象中截取,替换等api方法都是返回新的字符串对象
- 字符串内部纸箱的char数组对象被final修饰,其指向不可被修改
4 包装类型常量池
- 通过=号给包装类型赋值时,均会隐式的调用其对应的valueOf()方法
- 包装类型的常量池机制是通过valueOf()方法实现
- Float,Double未实现常量池
- Boolean类型常量只有两个值True,False
- Byte,Short,Long中均实现-128~127的常量池
- Integer默认支持-128~127常量池,可设置配置项常量池范围
Integer通过配置项java.lang.Integer.IntegerCache.high调整常量池最大值
- Character实现0~127常量池