Javaintern() 引发的问题-思考与解决
Posted Peter-OK
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Javaintern() 引发的问题-思考与解决相关的知识,希望对你有一定的参考价值。
【举例解释】在调用”ab”.intern()方法的时候会返回”ab”,但是这个方法会首先检查字符串池中是否有”ab”这个字符串,如果存在则返回这个字符串的引用,否则就将这个字符串添加到字符串池中,然会返回这个字符串的引用。
在看如下两段程序
【程序一】
// 运行环境要 JDK7及以上
String s1 = new String("1");
s1.intern();
String s2 = "1";
System.out.println(s1 == s2); // false
【程序二】
// 运行环境要 JDK7及以上
String s1 = new String("1") + new String("1");
s1.intern();
String s2 = "11";
System.out.println(s1 == s2); //true
针对运行结果,同样都是s1调用intern()方法,s2在常量池中,s1和s2比较,却是一个返回false一个返回true ???
【现象分析】
打印 hashcode【JDK 7+】
【程序一】
String s1 = new String("1");
System.out.println(s1+"->"+System.identityHashCode(s1));// 1->272890728
String s11 = s1.intern();
System.out.println(s11+"->"+System.identityHashCode(s11));// 1->1596879151
String s2 = "1";
System.out.println(s2+"->"+System.identityHashCode(s2));// 1->1596879151
System.out.println(s1 == s2); // false
【程序二】
String s1 = new String("1") + new String("1");
System.out.println(s1+"->"+System.identityHashCode(s1));//11->272890728
String s11 = s1.intern();
System.out.println(s11+"->"+System.identityHashCode(s11));//11->272890728
String s2 = "11";
System.out.println(s2+"->"+System.identityHashCode(s2));//11->272890728
System.out.println(s1 == s2); //true
打印 hashcode【JDK 6】
【程序一】
String s1 = new String("1");
System.out.println(s1+"->"+System.identityHashCode(s1));// 1->272890728
String s11 = s1.intern();
System.out.println(s11+"->"+System.identityHashCode(s11));// 1->1596879151
String s2 = "1";
System.out.println(s2+"->"+System.identityHashCode(s2));// 1->1596879151
System.out.println(s1 == s2); // false
【程序二】
String s1 = new String("1") + new String("1");
System.out.println(s1+"->"+System.identityHashCode(s1));//11->272890728
String s11 = s1.intern();
System.out.println(s11+"->"+System.identityHashCode(s11));//11->1596879151
String s2 = "11";
System.out.println(s2+"->"+System.identityHashCode(s2));//11->1596879151
System.out.println(s1 == s2); //false
在JDK 6 时,【程序一】和【程序二】执行过程是相似的,我们来分析一下,
【程序一】String s1 = new String("1"); 这种方式同时会生成两个对象:堆中的"1"对象 以及常量池中"1"对象(构造器中传入的 "1",在字符串常量池中)。但是此时s1是指向堆中的对象地址。
调用 s1.intern() ,字符串常量池已经存在"1"对象,所以直接返回。
s2 指向的就是字符串常量池中 “1” 的对象地址。
【程序二】String s1 = new String("1") + new String("1"); 这种方式同时会生成四个对象:2个堆中的"1"对象和1个常量池中"1"的对象(构造器中传入的 "1",在字符串常量池中)和1个s1指向的堆中的对象。但是此时s1是指向堆中的对象地址。
调用 s1.intern() ,字符串常量池中还没有"11"对象,所以会在字符串常量池中创建一个对象并保存。
s2 指向的就是字符串常量池中"11"的对象地址。
而到了JDK7后,【程序一】没有变化。
【程序二】在调用方法intern()时,虽然字符串常量池中还没有"11"对象,但已存在于Java堆中,那么就不再在字符串常量池中创建一个对象,而是将堆中"11"对象的引用保存到字符串常量池中。
s2 指向的就是字符串常量池中保存的 对堆中"11"对象的引用。
所以判断为 true。
后记:为什么从JDK6到JDK7会发生这种变化,正式(错别字哦,致敬作者^_^)因为“……在 Jdk6 以及以前的版本中,字符串的常量池是放在堆的 Perm 区的,Perm 区是一个类静态的区域,主要存储一些加载类的信息,常量池,方法片段等内容,默认大小只有4m,一旦常量池中大量使用 intern 是会直接产生java.lang.OutOfMemoryError: PermGen space
错误的。 所以在 jdk7 的版本中,字符串常量池已经从 Perm 区移到正常的 Java Heap 区域了……”摘自文章《深入解析String#intern》,分析的很详细
补充:【JDK8打印 hashcode】
【程序一】
String s1 = new String("1");
System.out.println(s1+"->"+System.identityHashCode(s1));// 1->272890728
String s11 = s1.intern();
System.out.println(s11+"->"+System.identityHashCode(s11));// 1->1596879151
String s2 = "1";
System.out.println(s2+"->"+System.identityHashCode(s2));// 1->1596879151
System.out.println(s1 == s2); // false
【程序二】
String s1 = new String("1") + new String("1");
System.out.println(s1+"->"+System.identityHashCode(s1));//11->1596879151
String s11 = s1.intern();
System.out.println(s11+"->"+System.identityHashCode(s11));//11->1596879151
String s2 = "11";
System.out.println(s2+"->"+System.identityHashCode(s2));//11->1596879151
System.out.println(s1 == s2); //true
以上是关于Javaintern() 引发的问题-思考与解决的主要内容,如果未能解决你的问题,请参考以下文章