目前关于字符串池(字符串常量池)在内存区域的位置大家都是没有争议的,JDK1.7以后,字符串池在堆里。但是字符串池里存储的内容有2种说法:一种是JavaGuide为主的,认为字符串池里既存了引用也存了对象;另一种是以R大为主的认为字符串池存储的只是字符串对象的引用。
字符串池里既存了引用也存了对象:
String s1 = new String("abc");这句话创建了几个字符串对象?
字符串池里存的只是引用:
请别再拿“String s = new String("xyz");创建了多少个String实例”来面试了吧
Java 中new String("字面量") 中 "字面量" 是何时进入字符串常量池的?
java基础:String — 字符串常量池与intern(二)
但是不论哪种观点,大家都认为语句
String str = new String("abc");
创建了2个对象。
只是字符串创建的位置不同:
- 一个在字符串池里,另一个在堆里
- 两个都在堆里
大家用来佐证的方法都用到了String.intern()方法:
String s1 = new String("计算机") + new String("技术");
s1.intern();
String s2 = "计算机技术";
System.out.println(s1 == s2);//true
我们来看一下不同观点关于该方法的解释。
字符串池里既存了引用也存了对象:
如果字符串常量池中已经包含一个等于此String对象内容的字符串,则返回常量池中该字符串的引用;如果没有,JDK1.7之前(不包含1.7)的处理方式是在常量池中创建与此String内容相同的字符串,并返回常量池中创建的字符串的引用,JDK1.7以及之后,字符串常量池被从方法区拿到了堆中,JVM不会在常量池中创建该对象,而是将堆中这个对象的引用直接放到常量池中,减少不必要的内存开销。(JavaGuide)
字符串池里存的只是引用:
- intern方法是Native调用,它的作用是在方法区中的常量池里寻找等值的对象,如果没有找到则在常量池中存放当前字符串的引用并返回该引用,否则直接返回常量池中已存在的String对象引用。
- a.intern()会在字符串池中查找是否有一个字符串引用所指向的对象的值等于a的值,如果有就直接返回池中的引用;如果没有,就把a指向的对象的引用放入池中,在返回该引用。
接下来我们来看不同观点下,字符串在内存中的存储情况。
字符串池里既存了引用也存了对象:
String str1 = "abc"; // 在字符串池里创建字面量对象"abc"及其引用
String str2 = new String("abc"); // 在堆里创建对象"abc"
System.out.println(str1 == str2); // false
StringTable存储引用并指向字符串池里创建的对象。
字符串池里存的只是引用:
String s = new String("2"); // 在堆里创建2个对象,s指向new出来的对象
s.intern();// 在字符串池里创建引用指向堆里的字面量对象"2"
String s2 = "2";// 字符串池里已经存在字面量对象"2"的引用,不再重复创建,s2指向已经存在的引用
System.out.println(s == s2);// false,s指向堆的new出来的对象,s2指向字符串池里的对象
String s3 = new String("3") + new String("3");// 在堆里创建3个对象,s3指向拼接后的对象
s3.intern();// 在字符串池里创建引用指向堆里的字面量对象"33"
String s4 = "33";// 字符串池里已经存在字面量对象"33"的引用,不再重复创建,s4指向已经存在的引用
System.out.println(s3 == s4);// true
这里的区别在于字符串池里是否包含堆里创建字面量的区域,如果单纯的认为StringTable就是字符串池(String Pool),那么字符串池确实只有引用,否则既保存了引用也保存了对象。
实际上,字面量是否在字符串池里重复引用,已经在编译阶段的class常量池里完成了。
欢迎大家批评指正!