String池化及intern()方法的作用
声明:以下内容可能有误
常量池
要理解String的池化,必须首先知道常量池这个概念。
方法区:存储Class文件编译后的类信息、常量、静态变量、编译后的代码
常量池:存储Class文件编译后的字面量和符号引用;但这个常量池又叫运行时常量池,则说明可以在程序运行期间将新的常量放入池中,如使用String类的intern()。那么为什么要使用常量池:通过对象共享的方式避免频繁的创建和销毁对象,从而节省空间,且节省运行时间(比如比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等)。
通俗的来说,Class文件中除常量表(constant pool table)之外的信息都会加载到方法区,而常量表中的信息都会加载到常量池中。
在JDK1.6及之前,常量池位于方法区中,而在JDK1.7及之后,常量池位于堆中。
String
String是不可变类,即一旦创建,就不能再次修改这个对象。而在编程中,String类型是最长用到的数据类型,对字符常量池化后,可以大大减少字符的数量,从而节省空间。
比如,通过如下方式声明两个String类型的变量,只会在内存中(常量池中)创建一个“abc”这个字符串,而s1和s2均会指向这个地址,所以,无论使用s1==s2还是s1.equals(s2),都会得到true的结果。
但是,如果使用new关键字来创建String对象,情况则会不同:
对于new String("abc")
这句代码需要这么来理解:在类加载的时候,就会将“abc”放在常量池中;在代码真正执行的时候,会从常量池中复制“abc”到堆中或者说在堆中创建“abc”这个字面量(如下面的字节码所示)。通过new这种方式创建String对象,与使用new创建其他普通对象是没有区别的,上述代码中的①和②均会在堆中创建对象,只不过这两个对象的内容都是“abc”,但内存地址不一样,所以,使用==来比较,结果为false。
前面说到,可以在运行期间动态地向常量池中添加常量,如果想要在运行期间向运行池中添加一个字符串,则可以使用String类中提供的一个方法:intern()
,intern
这个单词是“驻留”的意思,也就是将某个字符串放置到常量池中。但是,如何放,不同JDK版本是不一样的(因为常量池的位置发生了变化)。
JDK1.6及之前:如果常量池中已经存在想要放入的字符串(
equals
),则直接返回已经存在的那个字符串的地址,如果不存在,则将其放入,并返回引用地址。JDK1.7及之后:如果常量池中不存在,但堆中存在,则直接在常量池中存储这个字符串在堆中的地址,而不是像JDK1.6那样存储这个字符串本身。
即通过intern()方法,可以确保从常量池中得到与想要放入的字符串值(假设为s)equals的一个字符串的地址(假设为s_addr),并且只要s是equals的,那么得到的永远是这个s_addr。
使用intern的场景:需要使用new String(String s) 大量创建String的对象的时候,使用new String(s.intern()) 来替代,可以节省内存空间。
TODO
关于intern()还有没能理解的地方,比如在JDK1.8中:执行以下代码,输出结果为fasle和true。
参考
Last updated