前言
看过jvm的朋友都知道,在线程共享区有方法区,方法区中的运行时常量池就是我们日常生活中所说的常量池。
运行时常量池,是jvm虚拟机在完成类装载后,将class文件中的常量池载入到内存中,并保存在方法区中。
运行时常量池相对于class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入class文件中的常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用比较多的就是String类intern()方法。
String的intern()方法会查找在常量池中是否存在一份equal相等的字符串,如果有则返回该字符串的引用,如果没有则添加自己的字符串进入常量池。
常量池的好处
常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。
例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。
* 节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。
* 节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。
字符串常量
最典型的案例就是:

String s1 = “Hello”;
String s2 = “Hello”;
String s3 = “Hel”+“lo”;
String s4 = “Hel”+new String(“lo”);
String s5 = new String (“Hello”);
String s6 = s5.intern();
String s7 = “H”;
String s8 = “ello”;
Stringi s9 = s7 + s8;
System.out.println(s1 == s2); //true
System.out.println(s1 == s3); //true
System.out.println(s1 == s4); //false
System.out.println(s1 == s5); //false
System.out.println(s1 == s6); //true
System.out.println(s1 == s9); //false

其中s1==s2结果为true,非常好理解,s1和s2都是字符串字面量,在编译期间,这种字面量会直接放入class文件的常量池中,从而达到复用,载入运行时常量池后,s1、s2指向的同一个内存地址,所以相等。
S1和s3相等,这个设计“+”链接符。字符串对象可以使用“+”连接其他对象。其中字符串连接是通过StringBuilder(或StringBuffer)类及其append方法实现的,对象转换为Srting是通过toString方法实现的。
如果“+”两端都是字符串常量时,编译期会进行相应的优化,直接将两个字符串常量拼接好。
所以s1和s3相等。
S1和s4不相等,很简单,虽然都是用“+”连接符,但是s4要有部分在运行时才能编译时才能知道结果,结合字符串不变定理,s4分配的地址肯定和s1不相同。
S1和s5不同,和s4类似,s5对象在运行时才会知道结果,所以s5的地址不会和s1相同。
S1和s6相同是因为intern会判断常量池中是否存在一份内内存容相等的结果,不是内存地址相等的结果,如果存在内存内容相当的结果则返回该字符串的引用。
S1和s9不相等是因为,s9指向的地址是s7+s8指向的运行后拼接在一起的结果的地址。
注意
被static final 修饰的字符串

public static final String a = "123”; 
public static final String b = "456”;
public static void main(String[] args) 
{
     String c = "123456”; 
     String d = a + b; 
     System.out.println(c == d); //true
}

通过static final修饰的静态变量,存放在方法区中,c也是放在方法区的常量池中。
被static修饰的代码块

public static final String a; 
public static final String b;

static {
    a = "123";
    b = "456";
}

public static void main(String[] args) 
{
     String c = "123456”; 
     String d = a + b; 
     System.out.println(c == d); //false
}

静态代码块会在运行期来初始化,存放在堆上,具体a和b指向的地址和C不相同。

One thought on “Java常量池”

发表评论

邮箱地址不会被公开。