我们先来看个例子:
public class StringTest { public static void main(String[] args) { String a = "A"; String b = new String("A"); System.out.println(a == b); // false String c = b.intern(); System.out.println(a == c); // true String d = new String("A"); System.out.println(b == d); // false System.out.println(b.intern() == d.intern()); // true }}
出现上述的结果,我们需要了解下,intern的作用:
(1) 当常量池中不存在"A"这个字符串的引用,将这个对象的引用加入常量池,返回这个对象的引用。
(2) 当常量池中存在"A"这个字符串的引用,返回这个对象的引用。如下图
字符串常量池
字符串常量池的位置
字符串常量池大家可以认为独立在运行时常量池之外,不要和运行时常量池混淆。字符串常量池应该是在堆中。
字符串常量池里放的是什么
字符串常量池里放的实际上是字符串对象的引用,而不是字符串对象,这个也是十分容易混淆的地方,字符串常量池里的引用指向堆中的字符串对象,但是堆中的字符串对象,只有一部分是被字符串常量池所引用的,还有一部分,我们称为不在常量池中,例如:
String b = new String("A");
因此,常见的一些面试题,我们就有了答案:
(1)现在当有人问 String str = new String(“abc”);创建了几个对象,常量池有abc字段是1个,常量池没有"abc"字段则是2个。
(2)String str=“abc”;创建了几个对象(如果常量池里面已经有对象了就是0个。如果没有就是1个);
(3)new String(“abc”).intern();创建了几个对象(如果常量池里面已经有该字符串对象了就是1个,如果没有就是两个)
了解了作用,那intern有什么用途呢?
1)使用==比较String对象
有人说,比较String用equals方法就行了,为什么使用intern后使用==来操作呢?
真的是这样吗,我们先看下equals方法:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
我们发现equals方法中对字符串比较真的是暴力,直接从头到尾挨个字符比较,如果字符串长度很大,前面字符相差不大,效率就会很低下,在性能要求很高的场景下,这种比较是不能容忍的。
那么我们在创建字符串的时候都是以intern()方法,那使用==比较就会效率高的多。
2)使用intern()方法,减少堆中创建过多的字符串对象,减少内存消耗
我们再来讨论下不同版本jdk中的intern()方法:
JDK1.6 和 JDK1.7及以上 在 intern() 方法的实现上,有相同,也有不同。
相同点:都先去查看字符串常量池是否有该字符串,如果有,则返回字符串常量池中的引用。不同点:JDK1.7+,当字符串常量池中找不到对应的字符串时,不会将字符串拷贝到字符串常量池,而只是生成一个对该字符串的引用在字符串常量池。而 JDK1.6 会拷贝字符串至字符串常量池