4.8 String、StringBuilder、StringBuffer
“哥,上一篇深入理解 String.intern() 讲到了 StringBuilder,这一节我们就来聊聊吧!”三妹很期待。
“好啊,它们之间的关系还真的是挺和谐的。”看着三妹好奇的样子,我感到学技术就应该是这个样子才对。
由于字符串是不可变的,所以当遇到字符串拼接(尤其是使用+
号操作符)的时候,就需要考量性能的问题,你不能毫无顾虑地生产太多 String 对象,对珍贵的内存造成不必要的压力。
于是 Java 就设计了一个专门用来解决此问题的 StringBuffer 类。
public final class StringBuffer extends AbstractStringBuilder implements Serializable, CharSequence {
public StringBuffer() {
super(16);
}
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
public synchronized String toString() {
return new String(value, 0, count);
}
// 其他方法
}
不过,由于 StringBuffer 操作字符串的方法加了 synchronized
关键字进行了同步,主要是考虑到多线程环境下的安全问题,所以执行效率会比较低。
于是 Java 就给 StringBuffer “生了个兄弟”,名叫 StringBuilder,说,“孩子,你别管线程安全了,你就在单线程环境下使用,这样效率会高得多,如果要在多线程环境下修改字符串,你到时候可以使用 ThreadLocal
来避免多线程冲突。”
public final class StringBuilder extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
// ...
public StringBuilder append(String str) {
super.append(str);
return this;
}
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
// ...
}
除了类名不同,方法没有加 synchronized,基本上完全一样。
实际开发中,StringBuilder 的使用频率也是远高于 StringBuffer,甚至可以这么说,StringBuilder 完全取代了 StringBuffer。
之前我们也曾聊过,Java 是一门解释型的编程语言,所以当编译器遇到 +
号这个操作符的时候,会将 new String("二哥") + new String("三妹")
这行代码编译为以下代码:
new StringBuilder().append("二哥").append("三妹").toString();
这个过程是我们看不见的,但这正是 Java 的“智能”之处,它可以在编译的时候偷偷地帮我们做很多优化,这样既可以提高我们的开发效率(+
号写起来比创建 StringBuilder 对象便捷得多),也不会影响 JVM 的执行效率。
当然了,如果我们使用 javap 反编译 new String("二哥") + new String("三妹")
的字节码的时候,也是能看出 StringBuilder 的影子的。
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: new #4 // class java/lang/String
10: dup
11: ldc #5 // String 二哥
13: invokespecial #6 // Method java/lang/String."<init>":(Ljava/lang/String;)V
16: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: new #4 // class java/lang/String
22: dup
23: ldc #8 // String 三妹
25: invokespecial #6 // Method java/lang/String."<init>":(Ljava/lang/String;)V
28: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava
真诚点赞 诚不我欺
回复