String
拼接
字符串的拼接分为两种情况:
-
字面量的拼接
编译器优化,直接视为拼接后的结果 -
存在变量的拼接
实际调用的是StringBuilder
不在存储在StringPool中而是直接在堆中创建新的String实例。
String a=new String(“aa”) 创建了几个对象?
创建了两个,因为new在堆空间中创建了一个,并且在字符串常量池中也创建了一个。
new String(“a”) + new String(“b”) 创建了几个对象?
创建了六个对象
- 第一个对象是StringBuilder
- new
- 字符串常量池中的"a"
- 同上
- 同上
- 在由StringBuilder完成字符串变量拼接完成后返回的 toString()方法中还会创建一个新的对象
值得注意的是在后面这个问题中并没有在字符串常量池中新建字符串常量 “ab”
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
再继续上面的问题
String a=new String("1")+new String("1");
a.intern();
String b="11";
System.out.println(a==b);
输出的其实是true。
- 变量a指向堆空间中的字符串对象的实例
- a执行intern方法将字符串"11"加入到字符串常量池中,返回的字符串常量池中字符串常量的地址无变量接收
- 变量b指向字符串常量池中的字符串常量"11"
看似没什么问题应该输出false。但是实际输出true的原因是:
- 在jdk7及以后由于放弃了永久代的概念改为了元空间,并且将字符串常量池存储在了堆中
- 变量a调用intern方法时,由于StringPool和变量a同处于堆中,jvm进行了空间的优化,不再在字符串常量池中创建字符串常量,而是在字符串常量池中添加了变量a的引用。
- 变量b确实是在字符串常量池中找到了字面量,但是其内容其实是堆空间中实例的引用。
- 值得注意的是,实例a调用的intern方法至关重要,如果不执行,返回的就是false,变量b的创建和以前一样在字符串常量池中找不到"11"的字面量,故此新建一个并返回。想想其实也合理,毕竟若不是实例直接调用intern方法时方便获得实例的引用,jvm总不能在每一个字符串添加进常量池之前都在偌大的堆空间中找找有没有相同内容的字符串。