在Java中,除了可以使用 String 类来存储字符串,还可以使用 StringBuilder 类
或 StringBuffer 类存储字符串,那么它们之间有什么区别呢?
String 类具有是不可变性。如
运行结果
从运行结果中我们可以看到,程序运行时会额外创建一个对象,保存
“helloworld”。当频繁操作字符串时,就会额外产生很多临时变量。使用
StringBuilder 或 StringBuffer 就可以避免这个问题。至于 StringBuilder 和
StringBuffer ,它们基本相似,不同之处,StringBuffer 是线程安全的,而
StringBuilder 则没有实现线程安全功能,所以性能略高。因此一般情况下,如
果需要创建一个内容可变的字符串对象,应优先考虑使用 StringBuilder 类。
那么如何定义 StringBuilder 类的对象呢? 我们来看下面的代码:
运行结果
imooc
StringBuffer和String的可变性问题
我们先看看这两个类的部分源代码:
//String
public final class String
{
private final char value[];
public String(String original) {
// 把原字符串original切分成字符数组并赋给value[];
}
}
//StringBuffer
public final class StringBuffer extends AbstractStringBuilder
{
char value[]; //继承了父类AbstractStringBuilder中的value[]
public StringBuffer(String str) {
super(str.length() + 16); //继承父类的构造器,并创建一个大小为str.length()+16的value[]数组
append(str); //将str切分成字符序列并加入到value[]中
}
}
很显然,String和StringBuffer中的value[]都用于存储字符序列。但是,
(1) String中的是常量(final)数组,只能被赋值一次。
比如:new String(“abc”)使得value[]={‘a’,‘b’,‘c’}(查看jdk String 就是这么实现的),之后这个String对象中的value[]再也不能改变了。这也正是大家常说的,String是不可变的原因 。
(2) StringBuffer中的value[]就是一个很普通的数组,而且可以通过append()方法将新字符串加入value[]末尾。这样也就改变了value[]的内容和大小了。
比如:new StringBuffer(“abc”)使得value[]={‘a’,‘b’,‘c’,’’,’’…}(注意构造的长度是str.length()+16)。如果再将这个对象append(“abc”),那么这个对象中的value[]={‘a’,‘b’,‘c’,‘a’,‘b’,‘c’,’’…}。这也就是为什么大家说 StringBuffer是可变字符串 的涵义了。从这一点也可以看出,StringBuffer中的value[]完全可以作为字符串的缓冲区功能。其累加性能是很不错的,在后面我们会进行比较。
总结,讨论String和StringBuffer可不可变。本质上是指对象中的value[]字符数组可不可变,而不是对象引用可不可变。
StringBuffer与StringBuilder的线程安全性问题
StringBuffer和StringBuilder可以算是双胞胎了,这两者的方法没有很大区别。但在线程安全性方面,StringBuffer允许多线程进行字符操作。这是因为在源代码中StringBuffer的很多方法都被关键字synchronized 修饰了,而StringBuilder没有。
了解了synchronized的含义以后,大家可能都会有这个感觉。多线程编程中StringBuffer比StringBuilder要安全多了 ,事实确实如此。如果有多个线程需要对同一个字符串缓冲区进行操作的时候,StringBuffer应该是不二选择。
注意:是不是String也不安全呢?事实上不存在这个问题,String是不可变的。线程对于堆中指定的一个String对象只能读取,无法修改。
总结
String针对的是少量字符串的操作。
当字符串较多,并且是多线程时,应当用StringBuffer(线程安全)。
当字符串较多,并且是单线程时,应当用StringBuilder。
运行速度比较
StringBuilder>StringBuffer>String