1.String类
String类没有apprend方法
String类的底层是通过char数组来保存字符串的。
StringBuffer类与StringBuilder类调用toString()方法可直接转为String类对象
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间。且String类是被final修饰的,所以它不能被重写。
String a = “a”; //假设a指向地址0x0001
a = “b”;//重新赋值后a指向地址0x0002,但0x0001地址中保存的"a"依旧存在,但已经不再是a所指向的,a 已经指向了其它地址。
因此String的操作都是改变赋值地址而不是改变值操作。
即对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象来覆盖原来的对象。
源码如下:
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > count) {
throw new StringIndexOutOfBoundsException(endIndex);
}
if (beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
}
return ((beginIndex == 0) && (endIndex == count)) ? this :
new String(offset + beginIndex, endIndex - beginIndex, value);
}
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
char buf[] = new char[count + otherLen];
getChars(0, count, buf, 0);
str.getChars(0, otherLen, buf, count);
return new String(0, count + otherLen, buf);
}
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = count;
int i = -1;
char[] val = value; /* avoid getfield opcode */
int off = offset; /* avoid getfield opcode */
while (++i < len) {
if (val[off + i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0 ; j < i ; j++) {
buf[j] = val[off+j];
}
while (i < len) {
char c = val[off + i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(0, len, buf);
}
}
return this;
注意
String str1 = “hello world”;和String str3 = “hello world”; 都在编译期间生成了 字面常量和符号引用,运行期间字面常量"hello world"被存储在运行时常量池(当然只保存了一份)。
通过这种方式来将String对象跟引用绑定的话,JVM执行引擎会先在运行时常量池查找是否存在相同的字面常量,如果存在,则直接将引用指向已经存在的字面常量;否则在运行时常量池开辟一个空间来存储该字面常量,并将引用指向该字面常量。
故str1==str3返回是为true的。
思考
1.String str = new String(“abc”); 创建了几个对象
分两种情况:
1.如果 abc 字符串之前没有用过,这毫无疑问创建了两个对象,一个是new String 创建的一个新的对象,一个是常量“abc”对象的内容创建出的一个新的String对象。
2.如果abc 字符串被引用,根据常量池的原理,则只会创建一个对象。
2.StringBuffer类
有apprend方法
StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。 每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。
StringBuffer 中append方法底层实现
思路:通过复制数组扩大char[]的容量,完成字符的拼接(就是把新的字符追加到char[])。如果要返回String类型,可以调用toString方法生成一个String方法。
java.lang.AbstractStringBuilder#append(java.lang.String)源码如下:
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
参考: StringBuffer 中append方法底层实现 https://www.cnblogs.com/zhangit/p/7886295.html
3.StringBuilder类
有apprend方法如
StringBuilder sh=new StringBuilder("jh");
sh.append("hahj");
System.out.println(sh);
输出为:jhhahj
StringBuffer和StringBuilder类功能基本相似,主要区别在于StringBuffer类的方法是多线程、安全的,而StringBuilder不是线程安全的
常见例子
package com.test;
public class testString {
public static void main(String[] args) {
StringBuffer a=new StringBuffer("A");
StringBuffer b=new StringBuffer("B");
testString t=new testString();
t.print(a, b);
System.out.println(a+","+b);
String c="fa";
String d="faa";
//String可以像基本数据那样用“=”赋值
d=c;
System.out.println(d);
}
public void print(StringBuffer x,StringBuffer y){
x.append(y);
// y=x;
//x.append(y)没有返回值
y=x.append(y);
}
}
输出结果为:
ABB,B
fa
4.总结
1.StringBuffer和StringBuilder类功能基本相似,主要区别在于StringBuffer类的方法是多线程、安全的,而StringBuilder不是线程安全的,而string是final修饰的,会创建新的对象。
2.如果是高并发下,且操作全局变量应该使用StringBuffer,方法内的局部变量用StringBuilder,因为栈内存是线程私有的。
参考资料
1、探秘Java中的String、StringBuilder以及StringBuffer https://www.cnblogs.com/dolphin0520/p/3778589.html