【Java】intern() 引发的问题-思考与解决

【举例解释】在调用”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

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值