我们可以new String对象来创建一个字符串
String s = new String(“sp”);
但是非常不建议这么用,可以使用字面量来替代
String s = “sp”;
这是为什么呢?我们知道new 关键字是会创建对象的,无论是否之前创建过
String a = new String("a");
String b = new String("a");
System.out.println(a == b);
结果输出false ,证明这是两个对象
那么使用字面量创建有什么好处呢?
String a = "a";
String b = "a";
System.out.println(a == b);
结果输出true,哦?有点意思了——他们指向同一个对象
也就是说,String b = “a”; 不再创建新的对象。
好像挺好的, 但是这是为什么呢?
在类加载的过程中,类的class文件的信息会被解析到内存的方法区里。
class文件里常量池里大部分数据会被加载到“运行时常量池”,包括String的字面量;但同时“Hello”字符串的一个引用会被存到同样在“非堆”区域的“字符串常量池”中,而"Hello"本体还是和所有对象一样,创建在Java堆中。
什么意思呢?就是java运行时的内存里 有一个叫做字符串常量池的地方,以字面量的方式创建的字符串,会被拷贝一份引用放在这里,当下次创建相同(equalse true)的字符串时,就直接返回这里的引用。
哦,那也就是说,我下次再定义 String c = “a” 是不会在内存创建另外一个字符串的,这样节约了内存呢
String.intern() 方法的作用
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
–from jdk 1.7
我来大概翻译一下,当调用Intern方法时,如果池中包含一个与此字符串对象『相等』的字符串,池里的字符串将会返回,否则这个字符串对象就会加入到池中,并且会返回一个此字符串对象的引用
String a = new String("a"); //堆中放对象 ,常量池中放引用
String a1 = a.intern(); //a1 = 字符串常量池中的引用
String b = "a"; //b = 字符串常量池中的引用
System.out.println(a1 == b); //true
结果返回 true
让我们来分析一下
String a = new String(“a”);会在堆中创建一个对象,并在常量池中放一个指向这个字符串对象的引用
String b = “a”; 返回指向常量池中字符串的引用
a.intern() 返回常量池中指向等于a(equalse(a))的字符串对象的引用
所以 a.intern() 和b 都是指向字符串常量池中的字符串对象
有点绕口…
在java 1.7 常量池不在是在永久代 而是在堆中
常量池中也不在存放字符串对象,而是对象的引用(既然常量池在堆中,那么就没必要再在常量池中放字符串对象了)
这样看来 第一次创建字符串时 new String 还是字面量是一样的,不过如果再new 相同的对象堆中会重复创建,而使用字面量不会
一个有意思的坑
String str1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern() == str1);
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str2);
java 1.7+
true false
str1很好理解 因为字符串是第一次出现 所以会在字符串常量池存一个引用 都指向堆里的对象
str2 很坑因为"java"并不是第一次出现,第一次是在sun.misc.Version,所以这里str2 是一个新的对象,堆中存在两个"java"对象