从相同点来看,这三者都可以用来表示字符串;从不同点看,它们在可变性、线程安全和性能上有着很不同的特性。
一、相同点
它们都可以用来表示字符串:
public static void main(String[] args){
String name1 = "zhangsan";
String name2 = new String("zhangsan");
StringBuilder sb1 = new StringBuilder("zhangsan");
StringBuffer sb2 = new StringBuffer("zhangsan");
}
二、不同点
2.1 可变性
2.1.1 String的不可变性
String一旦用来表示一个字符串,那么这个字符串所对应的地址中的内容就是不可更改的。
public static void main(String[] args){
String name1 = "zhangsan";
}
如上代码中,name1只是一个引用地址,这个地址中的内容是“zhangsan”,而该内容是不允许被更改。这个可以通过阅读String的源码得以确认,如下代码中value[]被声明为final类型,即不可更改。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
我们使用如下代码更改的只是引用地址,使得引用指向一个新的字符串地址,而原来的字符串地址中的内容还是保持原样。
public static void main(String[] args){
String name1 = "zhangsan";
String name2 = "lisi";
name1 = name2;
}
2.1.2 StringBuilder和StringBuffer的可变性
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
阅读源码,发现StringBuilder和StringBuffer都是继承AbstractStringBuilder,而AbstractStringBuilder中的value并没有声明为final类型,因此其值是允许被改变的。
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
2.2 线程安全
2.2.1 String是线程安全的
String所指向的值是不可改变的,因此天然是线程安全的。
2.2.2 StringBuilder不是线程安全的
我们可以做个试验来验证。
// 待补充
2.2.3 StringBuffer是线程安全的
将如上的代码中sb1替换为StringBuffer类型进行相同的测试,不管执行多少次,得出的结果都是确定的。
StringBuilder和StringBuffer为什么会有这样的区别呢?我们可以看看相同方法在各自源代码中的区别:
// AbstractStringBuilder源码,StringBuilder由此继承
@Override
public int length() {
return count;
}
// StringBuffer源码,重写了AbstractStringBuilder中的相同方法
@Override
public synchronized int length() {
return count;
}
可以看出,StringBuffer对方法进行了重写,增加了synchronized
关键字,对方法进行同步控制,从而实现线程安全。
2.3 性能
String因为不可改变其值,当给它赋予新的字符串时,实际是让其指向一个新的字符串地址,因此相对于StringBuilder和StringBuffer而言,空间和性能都有浪费。
StringBuffer相对于StringBuilder而言,增加了线程安全的控制,因此在性能上也会不如StringBuilder。
三、使用总结
- 操作少量的字符串数据的时候,可以考虑String;
- 单线程场景下操作大量字符串数据的时候,考虑使用StringBuilder;
- 多线程场景下操作大量字符串数据的时候,考虑使用StringBuffer;