简单理解String的 intern() 方法

提示:
对于接触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" 这样出现率高的字符串,在虚拟机启动的时候,已经使用过了。

参考:

String.intern()性能

你可能感兴趣的:(笔记,字符串,java)