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() 引发的问题-思考与解决的主要内容,如果未能解决你的问题,请参考以下文章

日常需求一次使用EasyExcel而引发的问题与思考~

日常需求一次使用EasyExcel而引发的问题与思考~

日常需求一次使用EasyExcel而引发的问题与思考~

MVC系列——一个异常消息传递引发的思考

MVC系列——一个异常消息传递引发的思考

常见函数错误引发的思考.