java学习笔记-验证jdk1.7+中String.intern()实际表现

网上大神直接拿JVM实现代码来看的,我没这个水平,只好自己写点验证程序来观察并总结一下结果。


/*
 *   jdk1.8
 */
public class StrIntern {
	
	private void print(String s1) {
		System.out.print(s1);
	}
	
	public void testIntern1() {
		String s1 = new StringBuilder("a").append("b").toString();
		System.out.println(s1.intern()==s1);   //true
		String s2 = "ab";
		System.out.println(s1.intern()==s1);  //true
		System.out.println(s2==s1);  //true
	}
	
	public void testIntern2() {
		String s1 = new StringBuilder("a").append("b").toString();
		String s2 = "ab";
		System.out.println(s1.intern()==s1);  //false
		System.out.println(s1.intern()==s2);  //true
	}

	public void testIntern3() {
		String s1 = new String("ab");
		System.out.println(s1.intern()==s1);  //false
		
	}	
	
	public void testIntern4() {
		String s1 = new StringBuilder("a").append("b").toString();
		String s2 = new StringBuilder("a").append("b").toString();
		System.out.println(s1.intern()==s1);   //true
		System.out.println(s2==s1);  //false
		System.out.println(s2.intern()==s1);  //true
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		StrIntern obj = new StrIntern();
		obj.testIntern4();  //观察结果

	}

}

按照网上大神说的,jdk1.7以后的版本字符串池不在放在方法区的常量池中,而是移到了堆中,这一点没法验证;按照《深入理解Java虚拟机》(第二版)书中的描述,第一次调用intern时,如果在字符串池中找不到相同的字符串(利用String.equals()方法判断是否相同),则把这个字符串的引用地址放入字符串池。


也就是说,放入字符串池的实际不是字符串常量,而是一个引用地址,这个地址可能指向常量池中的字符串常量,也可能指向堆中的一个字符串对象。(网上大神用了StringTable来描述字符串池,可以简单将其理解成一个引用映射表)


testIntern1方法里面我们通过append方式组合了一个值为“ab”的字符串对象s1(后面我们就会看到为什么用这种方式),在intern之后,字符串池中保存的就是这个引用,所以实际上s1.intern()就是s1(了解JVM内存区域就可以了解到它们一个存在于StringTable里,一个存在于方法的栈帧Frame的局部变量表里)它们都指向同一个字符串对象,这个对象保存在堆里,而不是在常量池里。


而s2==s1,这个就很有趣了。事实上,这就跟


String s1 = "1";
String s2 = "1";
sysout(s1==s2); //true

一个意思,给s2赋值时,JVM一看字符串池里已经有一个符合条件的引用了,就直接把这个引用丢给了s2,导致了s2==s1。


testIntern2方法中,我们在intern之前有一条String s2 = "ab";语句,这就不对了。这条语句可以说隐含了一个intern()方法,他其实是先在字符串池里去找一个值equals“ab”的引用,但是并没有找到(因为我们之前new的那位兄弟在堆里呢,并没有在字符串池里保存引用,因为没intern)于是JVM在常量池里创建了一个值为“ab”的字符串常量,然后把这个常量的引用丢进了字符串池里。然后我们又调了s1.intern(),JVM到字符串池里一找,找到了这个指向常量的引用,它和s2指向同一个常量"ab"。


testIntern3方法中,由于我们在new String()的参数中使用了字符串常量"ab",所以相当于隐世地执行了一句“ab”.intern(),运行结果自然是false



testIntern4方法中,前面和1方法的前半一致不再赘述,但是s2是new出来的,他指向了一个存放在堆里的新对象,所以s1==s2自然是false。但是如之前所说,因为这两个字符串的值是相等的,s2.intern()的时候JVM一看:通过了equals方法嘛,直接就把字符串池里的引用返回了,而这个引用恰好就是跟s1指向地址相同的。


(补充:Oracle JDK中默认的虚拟机是HotSpot,它的引用reference实际实现就是个指针pointer。)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值