官方API:
intern
public String intern()
返回字符串对象的规范化表示形式。
一个初始时为空的字符串池,它由类 String 私有地维护。
当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并且返回此 String 对象的引用。
它遵循对于任何两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。
所有字面值字符串和字符串赋值常量表达式都是内部的。
返回:
一个字符串,内容与此字符串相同,但它保证来自字符串池中。
------------------------------------------------------------------------------------------------
我们知道,一个Java程序运行后,String类会在内存的方法区中维护一个字符串池。对一个字符串调用intern()方法后,会先检查池内是否有该字符串,若有则返回;若没有没有则先创建再返回,确保返回的字符串已经以字面量的形式存在于池中。
上测试样例代码:
public class Test {
public static void main(String argv[])
{
String s1 = "HelloWorld";
String s2 = new String("HelloWorld");
String s3 = "Hello";
String s4 = "World";
String s5 = "Hello" + "World";
String s6 = s3 + s4;
System.out.println(s1 == s2);
System.out.println(s1 == s5);
System.out.println(s1 == s6);
System.out.println(s1 == s6.intern());
System.out.println(s2 == s2.intern());
}
}
测试结果:
false
true
false
true
false
解释一下:s1 创建的 HelloWorld 存在于方法区中的常量池其中的字符串池,而 s2 创建的 HelloWorld 存在于堆中,故第一条 false 。
s5 的创建是先进行右边表达式的字符串连接,然后才对 s5 进行赋值。赋值的时候会搜索池中是否有 HelloWorld 字符串,若有则把 s5 引用指向该字符串,若没有则在池中新增该字符串。显然 s5 的创建是用了 s1 已经创建好的字面量,故 true 。
第三个比较容易弄错,s6 = s3 + s4; 其实相当于 s6 = new String(s3 + s4); s6 是存放于堆中的,不是字面量。所以 s1 不等于 s6 。
第四个 s6.intern() 首先获取了 s6 的 HelloWorld 字符串,然后在字符串池中查找该字符串,找到了 s1 的 HelloWorld 并返回。这里如果事前字符串池中没有 HelloWorld 字符串,那么还是会在字符串池中创建一个 HelloWorld 字符串再返回。总之返回的不是堆中的 s6 那个字符串。
第四条也能解释为什么第五条是 false 。s2是堆中的 HelloWorld,s2.intern() 是字符串池中的 HelloWorld 。
--------------------------------------------------------------
在多一句,如果把
String s6 = s3 + s4;
改成
String s6 = (s3 + s4).intern();
会发生什么呢?
s6 存储的 HelloWorld 是存放字符串池中还是堆中呢?
答案是前者。
另外:
// jdk1.6
// false、true、false、true、false、true
// jdk1.7
// false、true、true、true、false、true
// jdk1.8
// false、true、true、true、false、true
String str1 = new StringBuffer("ja").append("va").toString();
String intern1 = str1.intern();
System.out.println(intern1 == str1);
System.out.println(intern1.equals(str1));
String str2 = new StringBuffer("我是").append("中国人").toString();
String intern2 = str2.intern();
System.out.println(intern2 == str2);
System.out.println(intern2.equals(str2));
String str3 = new StringBuffer("我是").append("中国人").toString();
String intern3 = str3.intern();
System.out.println(intern3 == str3);
System.out.println(intern3.equals(str3));
/**
* "java"字符串常量比较特殊,它是固定存在字符串常量池中,所以都是返回了false
* Java6中字符串常量池是在永久代区,intern方法会将首次遇到的字符串复制到那里,返回的也是永久代中这个字符串的引用。不建议使用这个方法。
* Java7中字符串常量池被放到了堆中,intern的实现也不再是复制实例,只是在常量池中记录首次出现的实例引用。
* Java8同7一致
*/
代码解释:
(1)java1.6
str1、str2和str3都是指向堆内存的地址;
intern1、intern2和intern3都是指向永生代的常量池地址;
所以,“==”的地址比较肯定都是false,但是equals比较的都是内容,所以都是true;
(2)java1.7
str1、str2和str3都是指向堆内存的地址
由于“java”字符串的特殊性,intern1指向的常量池的地址,所以第一个地址的比较就是false;
intern2和str2都是指向同一块堆地址,所以第二个地址的比较就是true;
由于str3的字符串已经在常量池中存在,所以intern3就和intern2、str2的地址保持一致;
原文:
https://blog.csdn.net/wjzhang5514/article/details/70209403