提示:
对于接触Java不久的小萌新,或者对String还不熟的朋友,可以先看一下上一篇博文:关于String的小知识点,预热一下,或许能够帮助你理解接下来的内容。
String.intern()原理
String.intern()是一个Native方法,底层调用C++的 StringTable::intern
方法。
原理:当调用 intern 方法时,如果常量池中已经有该字符串,则返回池中的字符串;否则将此字符串添加到常量池中,然后返回常量池中字符串的引用。
String.intern()在JDK6与JDK7下的差异:
JDK6中:
常量池在永久代分配内存,永久代和Java堆的内存是物理隔离的,执行intern方法时,如果常量池不存在该字符串,虚拟机会在常量池中复制该字符串,并返回引用。所以需要谨慎使用intern方法,避免常量池中字符串过多,导致性能变慢,甚至发生PermGen内存溢出。
JDK7中:
常量池已经在Java堆上分配内存,执行intern方法时,如果常量池已经存在该字符串,则直接返回字符串引用,否则复制该字符串对象的引用到常量池中并返回,所以在JDK7中,可以重新考虑使用intern方法,减少String对象所占的内存空间。
举个栗子理解一下String.intern():
(以下测试均在JDK8环境下进行)
String str3 = new String("Hello") + new String("World");
str3.intern();
String str4 = "HelloWorld";
System.out.println(str3 == str4);
结果:true
解析:
str3 在常量池中生成"Hello"、“World"字符串,并在堆中生成str3的引用。但是需要注意:此时常量池中没有"HelloWorld",所以在执行 intern() 方法时,因为此时常量池中没有"HelloWorld”,所以将堆中str3对象的引用添加到常量池中,所以可以认为此时常量池中,已经存在字符串"HelloWorld"。因此 str4 指向的是常量池中str3对象的引用。即str3和str4指向的是同一对象。故结果为true。
即 str3指向堆,常量池中”HelloWorld"是str3对象的引用,也指向堆,str4指向常量池的引用,间接指向堆。
> 变式:
String str3 = new String("Hello") + new String("World");
String str4 = "HelloWorld";
String str = str3.intern();
System.out.println(str3 == str4);
System.out.println(str == str3);
System.out.println(str == str4);
结果:false false true
解析:
本题与上一道的区别在于intern()执行的位置。
在执行 String str4 = "HelloWorld";
时,常量池中还没有“HelloWorld"这个字符串,常量池中会生成一个"HelloWorld"字符串"。所以在执行 String str = str3.intern();
时,str 指向的是常量池中的字符串(注意是字符串,不是对象引用,要与上一题区分开)
即str3指向堆,str4指向常量池字符串常量“HelloWorld”,str指向常量池字符串常量“HelloWorld”。
常见栗子:
这些是比较简单的变式,与上同理,练练手加深理解
String str1 = new String("HelloWorld"); //堆一份,常量池一份,str1指向堆
String str = str1.intern(); //常量池已存在,返回池中字符串引用
String str2 = "HelloWorld"; //常量池已存在,故指向常量池
System.out.println(str1 == str2); //false
System.out.println(str == str2); //true
String str1 = new StringBuilder().append("Hello").append("World").toString();
str1.intern();
String str2 = "HelloWorld";
System.out.println(str1 == str2); //true
String str1 = new StringBuilder().append("HelloWorld").toString(); //指向堆
str1.intern(); //常量池已存在,返回池中字符串引用
String str2 = "HelloWorld";
System.out.println(str1 == str2); //false
比较特殊的栗子:
String s2 = new StringBuilder().append("Ja").append("va").toString();
System.out.println(s2.intern() == s2); //false
因为像 ”Java" 这样出现率高的字符串,在虚拟机启动的时候,已经使用过了。