tern() == t.intern()</code> is <code>true</code>
* if and only if <code>s.equals(t)</code> is <code>true</code>.
* <p>
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* <cite>The Java™ Language Specification</cite>.
*
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
*/
public native String intern();
字符串常量池的位置也是随着jdk版本的不同而位置不同。在jdk6中,常量池的位置在永久代(方法区)中,此时常量池中存储的是对象。在jdk7中,常量池的位置在堆中,此时,常量池存储的就是引用了。
在jdk8中,永久代(方法区)被元空间取代了。这里就引出了一个很常见很经典的问题,看下面这段代码。
@Test
public void test(){
String s = new String("2");
s.intern();
String s2 = "2";
System.out.println(s == s2);
String s3 = new String("3") + new String("3");
s3.intern();
String s4 = "33";
System.out.println(s3 == s4);
}
//jdk6
//false
//false
//jdk7
//false
//true
这段代码在jdk6中输出是false false,但是在jdk7中输出的是false true。我们通过图来一行行解释。
JDK1.6
- String s = new String("2");创建了两个对象,一个在堆中的StringObject对象,一个是在常量池中的“2”对象。
- s.intern();在常量池中寻找与s变量内容相同的对象,发现已经存在内容相同对象“2”,返回对象2的地址。
- String s2 = "2";使用字面量创建,在常量池寻找是否有相同内容的对象,发现有,返回对象"2"的地址。
- System.out.println(s == s2);从上面可以分析出,s变量和s2变量地址指向的是不同的对象,所以返回false
- String s3 = new String("3") + new String("3");创建了两个对象,一个在堆中的StringObject对象,一个是在常量池中的“3”对象。中间还有2个匿名的new String(“3”)我们不去讨论它们。
- s3.intern();在常量池中寻找与s3变量内容相同的对象,没有发现“33”对象,在常量池中创建“33”对象,返回“33”对象的地址。
- String s4 = "33";使用字面量创建,在常量池寻找是否有相同内容的对象,发现有,返回对象"33"的地址。
- System.out.println(s3 == s4);从上面可以分析出,s3变量和s4变量地址指向的是不同的对象,所以返回false
JDK1.7
- String s = new String("2");创建了两个对象,一个在堆中的StringObject对象,一个是在堆中的“2”对象,并在常量池中保存“2”对象的引用地址。
- s.intern();在常量池中寻找与s变量内容相同的对象,发现已经存在内容相同对象“2”,返回对象“2”的引用地址。
- String s2 = "2";使用字面量创建,在常量池寻找是否有相同内容的对象,发现有,返回对象“2”的引用地址。
- System.out.println(s == s2);从上面可以分析出,s变量和s2变量地址指向的是不同的对象,所以返回false
- String s3 = new String("3") + new String("3");创建了两个对象,一个在堆中的StringObject对象,一个是在堆中的“3”对象,并在常量池中保存“3”对象的引用地址。中间还有2个匿名的new String(“3”)我们不去讨论它们。
- s3.intern();在常量池中寻找与s3变量内容相同的对象,没有发现“33”对象,将s3对应的StringObject对象的地址保存到常量池中,返回StringObject对象的地址。
- String s4 = "33";使用字面量创建,在常量池寻找是否有相同内容的对象,发现有,返回其地址,也就是StringObject对象的引用地址。
- System.out.println(s3 == s4);从上面可以分析出,s3变量和s4变量地址指向的是相同的对象,所以返回true。
3. String.intern()的应用
在大量字符串读取赋值的情况下,使用String.intern()会大大的节省内存空间。
static final int MAX = 1000 * 10000;
static final String[] arr = new String[MAX];
public static void main(String[] args) throws Exception {
Integer[] DB_DATA = new Integer[10];
Random random = new Random(10 * 10000);
for (int i = 0; i < DB_DATA.length; i++) {
DB_DATA[i] = random.nextInt();
}
long t = System.currentTimeMillis();
for (int i = 0; i < MAX; i++) {
//arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length]));
arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
}
System.out.println((System.currentTimeMillis() - t) + "ms");
System.gc();
}
运行的参数是:-Xmx2g -Xms2g -Xmn1500M 上述代码是一个演示代码,其中有两条语句不一样,一条是使用 intern,一条是未使用 intern。发现不