目录
0、写在前面
本节主要分析String Constant Pool(SCP),即字符串常量池;该内容原属于软件构造系列文章Chapter8,由于比较重要,在此特别分析。
1、字符串常量池介绍
字符串常量池是Heap中包含字符串对象引用的特殊内存区域。
2、创建字符串的两种方式
创建一个字符串有两种方式
(1)使用字面量(保存在字符串常量池中,所有相同内容的字符串引用相同)
String s = "java";
每当我们创建字面量时,JVM都会检查字符串常量池。如果该字符串已存在于池中,则会提取其引用。 -如果该字符串不在池中,则会在其中创建一个新的字符串对象。
(2)使用关键字new
(保存在heap,即使内容相同,引用也不同)
String s = new String("java");
当我们使用new关键字创建字符串时,它会保存到堆中。
举个例子
其中s1==s2
结果为true
,引用相同;s1==s3
结果为false
,引用不同;当然s1.equals(s2)
和s1.equals(s3)
均为true
,因为字符串本身相同。
esp. intern()
可以将new创建的String对象转为字面量。
String a = new String("a");
String b = a;
String c = "a";
/* false, 此时b是堆中的a的引用, c在字符串常量池 */
System.out.println(b == c);
b = a.intern();
/* true, b引用字符串常量池中的c */
System.out.println(b == c);
/* false, a.intern()操作没有改变a对象 */
System.out.println(a == c);
调用方法a.intern()
的时候会返回"a"
,但是这个方法会首先检查字符串常量池池中是否有"a"
这个字符串,如果存在则返回这个字符串的引用,否则就将这个字符串添加到字符串池中,然会返回这个字符串的引用。
3、String & StringBuilder & StringBuffer
考虑如下代码
String s = "";
for(int i = 0; i < 1000; i++){
s = s + "a";
}
s = s + "a"
会在字符串常量池中创建"a"
, "aa"
, "aaa"
, …,产生大量的临时对象,十分耗费空间。
引入StringBuilder
,
StringBuilder s = new StringBuilder();
for(int i = 0; i < 1000; i++){
s.append("a");
}
总共只创建了一个String
对象。
引入StringBuffer
,
StringBuffer
与StringBuilder
几乎没有差别,主要在于StringBuffer
成员方法前多了关键字synchronized
,因此是线程安全的。
4、Q & A
由于字符串常量池存在,可以很大程度上节省空间,因此会有一些特殊情况。
Q:String s = "a" + "b"
创建了几个String对象?
A:一个。”a”, “b” 都是常量,在编译的时候直接优化为一个常量”ab”。
Q:String s = new String("a")
创建了几个String对象?
A:两个,首先创建一个字面量”a”,然后将字面量”s”作为参数,创建一个字符串对象s。(其实在栈上还有一个指向s的引用,不过,不是字符串对象)
Q:
String s = new String("a" + "b");
Line 3会创建几个String对象?
A:两个,编译器优化直接创建字面量”ab”,之后创建s对象。
Q:
String a = "s" + new String();
String b = "s" + new String();
System.out.println(a == b)
A:打印结果为false
,此时a和b为两个字符串对象。
参考:
Java intern() 方法
探秘Java中的String、StringBuilder以及StringBuffer
String s=”a”+”b”+”c” 创建了几个对象(评论很有意思)
String s=new String(“abc”) 创建了几个对象?
Java细节:字符串的拼接
Java中的字符串常量池