还记得String、StringBuilder、StringBuffer区别、String底层原理、实例化、拼接、比较吗?如果忘记可以到这里重温复习String、StringBuilder、StringBuffer区别;String底层详解,实例化、拼接、比较;String为什么不可变
有两种方式创建String对象:字面量赋值、new关键字
只要使用new方法,不管字符串常量池中是否存在"abc",都会在堆中创建新的对象(注意 和字符串常量池中的"abc"相区分),方式一的效率高于方式二
由于new关键字会在堆中开辟空间,因此开发中一般不建议使用,直接用字面量形式赋值即可。
经过上文讲解,我们就知道两者区别在于 创建对象个数不同
在Java中从".java"文件编译成".class"文件,会有一个优化器去优化我们的代码。
public class StringExercise05 {
public static void main(String[] args) {
String a = "ab"; //创建a对象
String b = "cd"; //创建b对象
//解读:先创建一个StringBuilder sb = new StringBuilder();执行 sb.append(a);执行sb.append(b);String c = sb.toString();
//等价于 (new StringBuilder()).append(a).append(b).toString()
//最后其实是 c 指向堆中对象的(String) value[]->池中 "abcd"
String c = a + b;
String d = "abcd";
System.out.println(c == d); //false
String e = "ab" + "cd";
System.out.println(d == e);//true
}
}
一共创建3个对象,内存布局图如下
查看源码 StringBuilder构造方法、append方法、
//StringBuilder构造方法
public StringBuilder() {
super(16);
}
//追溯到父类AbstractStringBuilder的构造方法,分配一个长度为16的char数组
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
//StringBuilder的append方法
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
//追溯到父类AbstractStringBuilder的append方法
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count); //getChars方法见下
count += len;
return this;
}
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
//我们的代码没有超过16个,故不会出现扩展value的情况。append使用arraycopy的复制方法,也没有产生新的对象,而StringBuilder的toString()方法通过前面数组new了一个新String
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
注意:
1)new String("ab")时,如果常量池中已有"ab",则不会在常量池中新建"ab",因为在常量池中只能存在一份相同的对象,即字符串常量池中不会存储相同内容的字符串
2)StringBuilder的toString(),在字符串常量池中,没有生成"ab"。如果前后文还有代码、并且字符串常量已在常量池中存在时,相同的常量将不会再创建,故至多6个对象。
参考 https://blog.csdn.net/xiaojin21cen/article/details/106404531
上文已介绍三种方式的原理
String a = "abc"; //字符串常量池
String b = "abc";
String c = new String("abc"); //new创建对象,堆和常量池中都会有该对象
String c1 = new String("abc");
String d = "ab" + "c"; //常量与常量拼接,结果在常量池中。查找常量池中是否存在"abc",如果存在 则让d直接引用
String e = new String("ab") + new String("c");
//a、b、c、c1、d、e 使用equals比较,由于字符串内容都相等 所以均会返回true
//故此处重点使用==,观察是否为同一个对象
System.out.println("a==b:" + (a==b)); //true
System.out.println("c==c1:" + (c==c1)); //false
System.out.println("a==c:" + (a==c)); //false
System.out.println("a==d:" + (a==d)); //true
System.out.println("c==d:" + (c==d)); //false
System.out.println("a==e:" + (a==e)); //false
System.out.println("c==e:" + (c==e)); //false
System.out.println("d==e:" + (d==e)); //false
String str1 = "ab";
String str2 = "cd";
String str3 = "ab" + "cd";
String str4 = "abcd";
System.out.println("str3==str4:" + (str3==str4)); //true
//内部实现 String temp = (new StringBuilder()).append(str1).append("cd").toString();
String str5 = str1 + "cd";
System.out.println("str5==str4:" + (str5==str4)); //false
//内部实现 String temp1 = (new StringBuilder()).append(str1).append(str2).toString();
String str6 = str1 + str2;
System.out.println("str6==str4:" + (str6==str4)); //false
str5 = str5.intern(); //将str5放进常量池,并将引用赋给原来的str5
System.out.println("str5==str4:" + (str5==str4)); //true
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。