1.intern方法
intern()方法可以在运行期间向字符串中动态加入字符串实例的方式,它的功能很简单,总结起来就一句话
可以在运行时向字符串池中添加字符串常量
添加的原则是,如果常量池中存在当前字符串,则直接返回常量池中它的引用;如果常量池中没有此字符串,则将此字符串的引用放入常量池,然后返回这个引用。
字符串进入常量池有两个途径:
1.字面量在编译器会进入Class的常量池,在类加载后会进入运行时常量池
2.使用intern()
String#intern()方法在JVM中是通过JNI调用C++实现的,其实里面调用的C++当中的StringTable的intern()方法,它的内部结构和HashMap类似,但是它不能扩容,默认大小是1009
如果字符串常量池的String非常躲,就会造成Hash冲突,从而导致链表会很长,它的查询性能将会从O(1)变成O(n),当调用intern方法时性能将会下降
在JDK6的版本中大小是固定的,在JDK7中可以通过参数来设置-XX:StringTableSize=12345
public static void main(String[] args) {String s = new String("1");s.intern();String s2 = "1";System.out.println(s == s2);String s3 = new String("1") + new String("1");s3.intern();String s4 = "11";System.out.println(s3 == s4);
}
s == s2 // false
s和s2比较分析:
String s = new String("1");
new String(“1”)同时创建了两个对象
一个对象是常量池中的"1"
一个是堆中的String对象
由于是new出来了的对象,s指向的是堆中的引用
s.intern();
调用intern方法,如果常量池中存在该字符串,则返回常量池引用,
如果不存在则将此字符串的引用加入到常量池中,然后返回
此时 常量池中已经有"1"这个字符串了,不需要再向常量池中添加,
所以这个地方返回的是常量池中的引用,另外返回的这个常量池的引用也并没有赋值给其他变量
String s2 = "1";
s2创建了一个"1"的字符串对象,这个时候会向StringTable(常量池)中查询是否存在该字符串,如果存在则返回这个引用,注意这个引用是常量池中的引用
System.out.println(s == s2);
s指向堆中的引用
s2指向的是常量池中的引用
在JDK6中,字符串常量池是放在Perm区域的,也就是放在方法区当中
方法区中的引用和堆中的引用,两者是属于不同的区域,必然是不相等的false
在JDK7中,字符串常量池移动到了堆中,原因是方法区的容量相比堆空间比较小,存储不了太多的常量,不过并不影响结果,仍然是两个区域内的对象进行比较仍然是false
s3和s4比较分析:
String s3 = new String("1") + new String("1");
s3在这里创建了两个对象,一个是堆中的"1"字符串对象,另一个两个new String(“1”) 拼接起来的"11"字符串对象放入到了堆中,但常量池中是没有这个"11"的
s3指向的是堆中的"11"对象
s3.intern();
接着s3调用intern方法,将"11"字符串对象放入到了字符串常量池中
在JDK6和JDK7的版本处理是不同的
在JDK6中,是复制堆中的字符串对象添加到字符串常量池中
在JDK7中,是复制堆中的字符串对象的引用添加到字符串常量池中
String s4 = "11";
s4在创建"11"字符串对象时,会先在StringTable中查询一番,如果有则返回常量池中的引用,如果没有则添加进去
在前面s3.intern()
步骤中,由于已经将"11"放入到了字符串常量池中,所以这里返回的是常量池中的引用
System.out.println(s3 == s4);
由于intern()方法在不同的JDK版本里面会有差异,所以它们的比较结果也是不同的
在JDK6中,s4指向的是常量池中引用(堆对象的副本),由于内存区域不一样,所以为false
在JDK7中,s4指向的是常量池中引用(堆对象的引用),s4虽然指向的也是常量池中的引用,但是常量池中存储的这个引用是堆对象的引用,所以两者在比较时是一样的,所以为true
下面还有一段代码,各位可以再思考下,结果是怎样的
public static void main(String[] args) {String s = new String("1");String s2 = "1";s.intern();System.out.println(s == s2);String s3 = new String("1") + new String("1");String s4 = "11";s3.intern();System.out.println(s3 == s4);
}