JDK1.8中字符串常量池的优化之String的intern()

目录

  • JVM的内存结构
  • String类
    • String类的结构
    • String对象的两种创建方式
    • String的intern()方法
      • 示例:String对象的基础使用
      • 示例:new的String对象调用intern()
      • 示例:intern()会不会改变调用对象本身
      • 示例:new的字符串对象相加后再调用intern()
      • 示例:类的String类型属性和字符串字面量比较

JDK每次升级都会做很多优化,我们使用最多的String常量类也在不断被优化。这次和大家分享的是JDK1.8中对String的优化之一,intern()方法的使用。


JVM的内存结构

先看看JVM的内存结构,观察一下常量池的位置:
JDK1.8中字符串常量池的优化之String的intern()_第1张图片

我们可以看到,常量池在JVM的内存结构中属于方法区。常量池是一个统称,细分的话有分为:Class常量池(静态常量池)、运行时常量池和字符串常量池。
在JDK1.8中,运行时常量池和字符串常量池逻辑上属于方法区,但它们实际存放位置又在堆中。
接下来,我们讨论一下字符串常量池的使用。


String类

理解字符串常量池的入口是我们平时使用最多的String类。


String类的结构

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
     
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

String类有两个成员变量char数组、hash值。String类被final关键字修饰,而且变量char数组也被final修饰了。
我们知道类被 final 修饰代表该类不可继承,而 char[]被private+final修饰,代表String对象不可被更改。


String对象的两种创建方式

  • String str = “abc”
    当代码中使用这种方式创建字符串对象时,JVM 首先会检查该对象是否在字符串常量池中,如果在,就返回该对象引用,否则新的字符串将在常量池中被创建。最后str引用常量池中的对象。这种方式可以减少同一个值的字符串对象的重复创建,节约内存。

  • String str = new String(“abc”)
    2、new对象的方式,如。
    代码编辑加载时,会在常量池中创建“abc”;在new对象时,引用常量池中的“abc”,并在堆中创建一个String对象。最后str引用堆中String对象。

对比后推荐使用第一种方式,简单且节省内存空间。


回到今天的主题,intern()方法。如果我们对着英文注释一句一句翻译,来理解它的话会很有限很苦涩,结合代码示例理解会更好一些(个人经验)。


String的intern()方法

String的intern()方法及其注释如下:

/**
* Returns a canonical representation for the string object.
*


* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
*


* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
*


* It follows that for any two strings {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code true}.
*


* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* The Java™ Language Specification.
*
* @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();

intern()是一个native方法,该方法的作用在注释中有描述,就是通过常量池复用技术来节省内存空间和重复创建的开销。

下面我们结合示例来理解intern()的使用及作用。


示例:String对象的基础使用

public static void main(String[] args) {
     

  String str1 = "abc";
  String str2 = "abc";
  String str3 = new String("abc");
  String str4 = new String("abc");
  System.out.println(str1 == str2); // true
  System.out.println(str3 == str4); // false
  System.out.println(str1 == str3); // false
  
}

这个示例,是平时大家使用最多的,没有异议。


示例:new的String对象调用intern()

public static void main(String[] args) {
     

   String str1 = "abc";
   String str2 = "abc";
   String str3 = new String("abc").intern();
   String str4 = new String("abc").intern();
   String str5 = new String("abc");

   System.out.println(str1 == str2); // true
   System.out.println(str3 == str4); // true
   System.out.println(str1 == str3); // true
   System.out.println(str1 == str5); // false
   System.out.println(str3 == str5); // false

}

分析,在intern()被调用时, “abc” 已经在字符串常量池中被创建了,new的String对象被intern()修饰后,str3、str4引用的是字符串常量池中的“abc” 。所以有了上面的结果。
intern()方法注释里也做了说明:

s.intern() == t.intern() is true if and only if s.equals(t) is true.

也是就是两个String对象,当且仅当它俩equals()比较值为true,那它俩intern()的“==”操作也为true。


示例:intern()会不会改变调用对象本身

public static void main(String[] args) {
     

  String str1 = new String("abc");
  String str2 = str1.intern();
  String str3 = "abc";
  System.out.println(str1 == str3); //false
  System.out.println(str2 == str3); //true
}

分析,str1引用的是堆内创建的String对象,str2、str3引用的是字符串常量池中的“abc”。值得关注的是str1调用intern()后,str1的引用并不会被改变,只会返回作用后的引用,如果需要使用这个引用就要赋值给新的String对象,用法上类似于String的replace方法。


示例:new的字符串对象相加后再调用intern()

public static void main(String[] args) {
     

   String str1 = new String("abc") + new String("abc");
   String str2 = str1.intern();
   String str3 = "abc" + "abc";

   String str4 = new String("abc") + new String("abc");
   String str5 = "abc" + "abc";

   System.out.println(str1 == str3); //true
   System.out.println(str2 == str3); //true
   System.out.println(str4 == str5); //false
}

上一个示例中,“str1 == str3”返回的是false;该示例的区别仅仅是,new的字符串对象相加后再调用intern(),“str1 == str3”返回的却是true。intern()作用到了相加后的对象的本身。
str4没有调用intern(),结果“str4 == str5”返回的是false。


示例:类的String类型属性和字符串字面量比较

public static void main(String[] args) {
     

     Teacher teacher = new Teacher();
     teacher.setName("abc");
     String name = teacher.getName();
     String str = "abc";
     System.out.println(name == str); // true
  }

  static class Teacher {
     
      private String name;
      private Integer age;

      /** get()/set() **/
  }
}

Teacher类的String类型属性值,引用的也是字符串常量池中的对象。


至此,本篇结束。


你可能感兴趣的:(Java,String)