Java中String的intern()方法,其设计的初衷就是重用String对象,以节省内存消耗。但是在JDK1.6和JDK1.7中intern()方法的功能有点点不同,具体怎么个不同法,且看看下面代码,这个例子是网上流传较广的一个例子:
String s1 = new String("1");
s1.intern();
String s2 = "1";
System.out.println(s1 == s2);
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
输出结果为:
JDK1.6:false false
JDK1.7:false true
为什么会是这种结果呢?接下来我们分别对JDK1.6和JDK1.7中的intern()方法进行分析:
1、JDK1.6代码分析
首先,new String("1")会在堆中新建一个内容为"1"的对象实例,同时会把该对象实例copy一份放到常量池中。s1的引用指向的是堆中新建的对象,s1.intern()方法会在常量池中查找是否已经存在s1所引用的对象,如果存在,则会返回一个引用指向常量池中的那个对象,否则会copy一份s1所引用对象的实例放到常量池中,并返回一个引用指向常量池中的那个对象实例。s2="1"这条语句执行的时候,由于"1"已经在常量池中存在了,所以s2的引用指向的是常量池中的对象实例,也就是说s1和s2的是两个不同的引用,所以第一个打印语句为false。
然后,new String("1")+new String("1")会在堆中新建两个内容为"1"的对象实例和一份内容为"11"的对象实例,同时会copy一份内容为"1"的对象实例放到常量池中。s3的引用指向的是堆中新建内容为"11"的对象实例,s3.intern()方法会在常量池中查找是否已经存在s3所引用的对象,由于常量池中没有"11"这个对象,因此会copy一份内容为"11"的对象放到常量池中,并返回一个引用指向常量池中的"11"对象实例。s4="11"这条语句执行的时候,由于"11"已经在常量池中存在了,所以s4的引用指向的是常量池中"11"的对象实例,因此s3和s4的是两个不同的引用,所以第二个打印语句也为false。
2、JDK1.7代码分析
同样的,new String("1")会在堆中新建一个内容为"1"的对象实例,同时会把该对象实例copy一份放到常量池中。s1的引用指向的是堆中新建的对象,但是intern()会有所不同,s1.intern()语句执行的时候,会在常量池中查找是否已经存在s1所引用的对象,如果存在,则会返回一个引用指向常量池中的那个对象,如果不存在,则会在常量池中记录一个引用指向s1所引用的对象实例,并返回该引用。s2="1"这条语句执行的时候,由于"1"已经在常量池中存在了,所以s2的引用指向的是常量池中的对象实例,同样的,s1和s2的是两个不同的引用,所以第一个打印语句为false。
然后,new String("1")+new String("1")会在堆中新建两个内容为"1"的对象实例和一份内容为"11"的对象实例,同时会copy一份内容为"1"的对象实例放到常量池中(注意:"11"的对象实例不会存入常量池)。s3的引用指向的是堆中新建内容为"11"的对象实例,s3.intern()语句执行的时候,会在常量池中查找是否已经存在s3所引用的对象,由于常量池中没有"11"这个对象,因此会在常量池中记录一个引用指向s3所引用的对象实例,并返回该引用。s4="11"这条语句执行的时候,由于常量池中已经存在了一个"11"对象实例的引用(s3指向的引用),所以常量池会把这个"11"对象实例的引用赋给s4,也就是说,s4的引用指向的是s3所指向的对象实例,因此s3和s4的是相同的引用,所以第二个打印语句为true。