Java 字符串常量池 及 intern 方法的使用
参考这篇文章:深入解析String#intern - 美团技术团队 (meituan.com)
jdk7 版本对 intern 操作和常量池都做了一定的修改,主要包括2点:
- 将String常量池 从 Perm 区移动到了 Java Heap区
- String#intern 方法时,如果存在堆中的对象,会直接保存堆中对象的引用,而不会重新创建对象。
下面做一个测试:
1 public static void main(String[] args) { 2 3 /* 4 * new String("a") + new String("b") 在堆和字符串常量池一共传创建了 5 个对象,分别为: 5 * 堆:"a"、"b"、"ab" + 字符串常量池:"a"、"b" 6 * 但 str1 是堆中 "ab" 对象的引用,并非字符串常量池 "ab" 对象的引用 7 * str1.intern() 实际是返回了字符串常量池 "ab" 对象的引用 8 * str3.intern() 也是返回了字符串常量池 "ab" 对象的引用 9 */ 10 11 // 返回堆中 "ab" 对象的引用 12 String str1 = new String("a") + new String("b"); 13 // 在字符串常量池中生成 "ab" 对象并返回其引用 14 String str2 = "ab"; 15 // 返回堆中新生成的 "ab" 对象的引用 16 String str3 = new String("a") + new String("b"); 17 // false, str1.intern() 返回字符串常量池中的引用,str1为堆中的引用,二者不是同一个内存地址 18 System.out.println(str1.intern() == str1); 19 // true, str1.intern() 和 str2 都是堆中同一个对象 "ab" 的引用 20 System.out.println(str1.intern() == str2); 21 // false, str1 和 str3 分别指向了堆中 2 个不同的 "ab" 对象 22 System.out.println(str1 == str3); 23 // true, str1.intern() 和 str3.intern() 都是字符串常量池中同一个对象 "ab" 的引用 24 System.out.println(str1.intern() == str3.intern()); 25 }
另一个测试:
1 public static void main(String[] args) { 2 /* 3 * 直接使用双引号声明出来的 String 对象会直接存储在常量池中,此时堆中生成一个 ”1“ 对象,字符串常量池中也生成一个 ”1“ 对象 4 * 返回的是堆中 ”1“ 对象的引用 5 */ 6 String s = new String("1"); 7 // 此时字符串常量池中已经存在 ”1“ 对象了,所以不会有堆中此对象引用拷贝的操作了 8 s.intern(); 9 // 此时字符串常量池中已经存在 ”1“ 对象了,直接返回字符串常量池中此对象的引用 10 String s2 = "1"; 11 // fasle, s 为堆中对象的引用,s2 为字符串常量池中对象的引用,二者内存地址不一样 12 System.out.println(s == s2); 13 14 // 在堆中生成 3 个对象,分别为 ”1“、”1“、”11“ 并返回堆中对象 ”11“ 的引用,此时常量池中是没有 ”11“ 对象的 15 String s3 = new String("1") + new String("1"); 16 // 把 s3 的引用拷贝到字符串常量池中 17 s3.intern(); 18 // 直接返回字符串常量池中 s3 的引用 19 String s4 = "11"; 20 // true,s3 和 s4 均为字符串常量池中的同一个内存地址 21 System.out.println(s3 == s4); 22 }