这个intern()方法简直是让人抓狂,搞了一天终于搞明白了(`・ω・´),简直爆炸。
这个方法在JDK 1.7做出了重大改变,先说一下在JDK 1.7以后版本里的情况。
在JDK 1.7中,intern()方法不会再复制实例,只是会把首次遇到的字符串实例的引用添加到常量池中(没有复制),并返回此引用。看如下示例:
public class Ex{ public static void main(String[] args){ // String st="javac"; String s1="jav"; s1=s1+"ac";//相当于是new了一个字符串对象"javac",放入到堆内存中。 //s1="jav"+"ac"是等价于s1="javac"的,并没有在堆内存中new一个对象。 System.out.println(s1.intern()==s1); String s2="ja"; s2=s2+"va"; System.out.println(s2.intern()==s2); String s3=new String("abc"); System.out.println(s3.intern()==s3); } }
运行结果:
s1是堆内存中字符串实例"javac"的引用,intern()方法会把此引用放到字符串常量池中,并返回这个引用,因此intern()返回的引用和s1这个引用是同一个。
按照同样的道理,第二个也应该是true啊,一直被这个示例搞晕了,后来去查了些资料,貌似是JVM在执行main方法之前会进行一系列的复杂操作,会把这个java当做一个字符串常量扔到常量池里面,所以常量池里面有这个字符串常量了,因此intern()返回的是常量池的引用,而s2这个是堆中的引用,所以是false。
第三个呢,在执行new String("abc")的时候,JVM会先把"abc"放到常量池中,然后才在堆中创建一个"abc"的实例,s3是指向堆内存的引用,intern()方法返回的是常量池的引用,所以是false。
如果我们把第一行的注释去掉,那么第一个打印的也会是false,因为javac会被先一步放入到常量池中。
下面说一下在JDK 1.6中的情况,在JDK 1.6中 intern() 方法会把首次遇到的字符串实例复制到常量池中,并返回此引用。所以针对上面的例子,打印出来的全是false,因为intern()返回的全是指向常量池的引用,而s1,s2,s3全是堆中对象的引用。
补充(JDK 1.8):
public class Ex1{ public static void main(String[] args){ String s1="jav"; s1=s1+"ac"; // String s3=s1.intern(); //语句1 String s2="javac"; String s3=s1.intern(); System.out.println(s1==s2); System.out.println(s2==s3); } }
输出结果:
s1是堆中的引用,在s1调用intern()方法之前执行了s2,所以会把"javac"字符串放到常量池中,所以s2是指向常量池的引用,这个时候再调用intern()方法就会返回常量池的引用,所以s3也是指向常量池的引用,所以第二个是true,第一个是false。如果把s3放到语句1的位置,则两个都是返回true。