直接创建字符串对象:
String s1 = "Hip Hop"
将创建一个字符串对象,但首先 JVM 检查字符串常量或文字 池,如果字符串不存在,它会创建一个新的字符串对象“Hip jop”,并在池中维护一个引用。该变量s1
也引用同一个对象。现在,如果我们在此之后添加一个声明:
String s2 = "Hip Hop"
JVM 首先检查字符串常量池,因为字符串已经存在,所以对池实例的引用返回到s2
.
System.out.println(s1==s2) // comparing reference and it will print true
java 可以进行这种优化,因为字符串是不可变的,并且可以共享而不必担心数据损坏。
使用 new 创建字符串对象
String s3 = new String("Hip Hop")
对于new
关键字,在堆内存中创建一个字符串对象,或者在池中不存在相等的字符串对象,s3
并将引用新创建的对象。
System.out.println(s3==s2) // two reference is different, will print false
使用运算符创建的字符串对象new
不引用字符串池中的对象,但可以使用 String 的intern()
方法。java.lang.String.intern()
返回一个实习字符串,即在全局字符串文字池中有一个条目的字符串。如果 String 尚未在全局 String 文字池中,则它将被添加到池中。
String s4 = s3.intern();
Systen.out.println(s4 == s2); // will print `true` because s4 is interned,
//it now have the same reference to "Hip Hop" as s2 or s1
但是尝试:
Systen.out.println(s4 == s3) // it will be false,
作为 的引用s4
,s2
和s1
是对池化实例的引用,同时是对堆内存s3
中创建的对象的引用。
使用 new 来创建字符串:
在 OpenJDK 7 Update 6 之前,JavaString.susbtring
方法存在潜在的内存泄漏。substring
方法将构建一个新的 String 对象,保留对整个 char 数组的引用,以避免复制它。因此,您可能会在不经意间保留对只有一个字符串的非常大的字符数组的引用。如果我们想在 之后有最少的字符串substring
,我们使用构造函数来获取另一个字符串:
String s2 = new String(s1.substring(0,1));
该问题已在 JDK 7 更新 6 中得到解决。因此,无需再创建字符串即可利用字符串文字池机制 new
提供的优势。
参考:
- 字符串文字池
- 使用 new 来防止使用子字符串的内存泄漏