不可变字符串String
String对象是不可变的。查看 JDK 文档你就会发现,String 类中每一个看起来会修改 String 值的方法,实际上都是重新创建了一个String对象,以包含修改后的字符串内容。
字符串拼接方法
- “ + ”操作符
代码如下:
public class Str {
public static void main(String[] args) {
String s="NIHAO";
s = s + "QWE";//此时会重新生成一个新的String对象
System.out.println(s);
}
}
将上述代码反编译后,我筛选了一下,如下:
如果你看不懂字节码指令没关系,可以看我的这篇文章字节码指令
public static void main(java.lang.String[] arg0);
0 ldc <String "NIHAO"> [2]
2 astore_1
3 new java.lang.StringBuilder [3]
6 dup
7 invokespecial java.lang.StringBuilder() [4]
10 aload_1
11 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [5]
14 ldc <String "QWE"> [6]
16 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [5]
19 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [7]
22 astore_1
23 getstatic java.lang.System.out : java.io.PrintStream [8]
26 aload_1
27 invokevirtual java.io.PrintStream.println(java.lang.String) : void [9]
30 return
从这个例子中,编译器创建了一个StringBuilder对象,用以构造最早的String,并以每个字符串调用一次StringBuilder的append()方法,总共2次,然后就会调用toString方法来返回一个新的String对象。
- concat 方法
代码如下:
public class Str {
public static void main(String[] args) {
String s="NIHAO";
s = s.concat("QWE");
System.out.println(s);
}
}
将上述代码反编译后,我筛选了一下,如下:
public static void main(java.lang.String[] arg0);
0 ldc <String "NIHAO"> [2]
2 astore_1
3 aload_1
4 ldc <String "QWE"> [3]
6 invokevirtual java.lang.String.concat(java.lang.String) : java.lang.String [4]
9 astore_1
10 getstatic java.lang.System.out : java.io.PrintStream [5]
13 aload_1
14 invokevirtual java.io.PrintStream.println(java.lang.String) : void [6]
17 return
反编译后好像看不了什么东西出来,因为concat也是java.lang.String里面的方法,所以的话我觉得可以直接去IDE看它的如何实现的。如下:
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
从concat的底部实现可以看到,其实它最终也是要new一个String对象的,所以前面为什么提到Sting的不可变性。顺便看看Arrays.copyOf的方法内部,如下:
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
- StringBuffer 方法
代码如下:
public class Str {
public static void main(String[] args) {
StringBuffer s=new StringBuffer("liuliu");
s = s.append("QWE").append("dsad");
System.out.println(s);
}
}
StringBuffer底层实现原理如下:
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
继续深入看看append方法内部如下:
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类继承了StringBufferAbstractStringBuilder,然后调用StringBufferAbstractStringBuilder类里面的那个append方法进行拼接字符串。append会直接拷贝字符到内部的字符数组中,如果字符数组长度不够,会进行扩展。
- StringBuilder 方法
代码如下:
public class Str {
public static void main(String[] args) {
StringBuilder s=new StringBuilder("liuliu");
s = s.append("QWE").append("dsad");
System.out.println(s);
}
}
StringBuilder底层实现原理如下:
public StringBuilder append(String str) {
super.append(str);
return this;
}
StringBuilder和StringBuffer类似的,区别就是在于StringBuffer是线程安全的,StringBuilder是线程不安全的。会发现StringBuffer调用append方法时,会比StringBuilder多了一个 toStringCache 字段。字段上的解释是返回最后一次toString的缓存值,一旦StringBuffer被修改就清除这个缓存值。而且StingBuffer的append方法中多了一个 synchronized 进行声明。
- StringUtils.join方法 这个建议看其他博主~
总结:效率比较 StringBuilder < StringBuffer < concat < + < StringUtils.join