常量池
- 为什么要使用常量池?
从内存上:合并相同的字面量,减少不必要的内存空间;
从时间上:对于字符串的比较,对常量池中的字符串使用“==”判断引用是否相等比equals快。
1. Class文件常量池
编译器间生成的字面量与符号引用1,在类加载后移至运行时常量池,保存在方法区中。
2. 运行时常量池
除Class常量池的内容,运行时也可将常量加入常量池中,存储在方法区中(元空间)。每个类都有一个。
3. 全局字符串常量池
全局字符串常量池在虚拟机中唯一,被所有类共享。存放在堆中
常量池加载过程
- 编译 —> 字面量与符号引用存入Class文件常量池
- 加载阶段 —> 转存入运行时常量池
- 准备阶段 —> 在堆中创建类变量的字符串实例,再将引用值存储到String Pool中,被所有类共享。
- 解析阶段 —> 将符号引用转换为直接引用,查询String Pool,以确保运行时常量池与全局字符串常量池的引用是一致的。
String.intern()方法与常量池
String a1="AA";
System.out.println(a1==a1.intern());
String a2=new String("B")+new String("B");
a2.intern();
String a3=new String("B")+new String("B");
System.out.println(a2==a3.intern());
System.out.println(a3==a3.intern());
String a4="BB";
System.out.println(a2==a4);
System.out.println(a2==a3);
String a5="B"+"B";
System.out.println(a4==a5);
/**
true:在堆中创建"AA"的实例,同时将引用放在常量池中;常量池中有值,所以a1.intern()返回的是a1的引用。
true:字符串连接符"+",本质上是调用了StringBuilder.append().toString方法。会在堆中创建"BB"对象,
调用intern()方法之后在常量池复制"BB"的引用,因此a3.intern()调用的是a2的引用。
flase:a3是自己在堆中创建的对象,a3.intern()是a2存储在常量池中的对象
true:a4直接调用常量池的引用
flase:a2与a3在堆中的对象不同
true:虚拟机针对字面量"B"+"B"会在编译器优化为"BB",因此相等。
*/
-
- 类和接口和全限定名:例如对于String这个类,它的全限定名就是java/lang/String。
- 字段的名称和描述符:所谓字段就是类或者接口中声明的变量,包括类级别变量(static)和实例级的变量。
- 方法的名称和描述符。所谓描述符就相当于方法的参数类型+返回值类型。