Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们都可以存储和操作字符串,区别:
1)可变性
String是不可变的,只读字符串,即String引用的字符串内容是不能被改变的。
StringBuilder和StringBuffer都继承自AbstractStringBuilder类(提供了很多修改字符串的方法比如append),所以StringBuffer和StringBuilder表示的字符串对象可以直接进行修改。
2)线程安全性
String中的对象是不可变的,可理解为常量,线程安全。StringBuffer对方法加了同步锁(synchronized)或者对调用的方法加了同步锁,所以线程安全。StringBuilder没有对方法进行加同步锁,所以是非线程安全。(Stingbuilder的扩容如下图所示)
3)性能
每次对 String
类型进行改变的时候,都会生成一个新的 String
对象,然后将指针指向新的 String
对象。StringBuffer
每次都会对 StringBuffer
对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder
相比使用 StringBuffer
仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
三者使用的场景:
- 操作少量的数据:适用
String
- 单线程操作字符串缓冲区下操作大量数据:适用
StringBuilder
- 多线程操作字符串缓冲区下操作大量数据:适用
StringBuffer
Stringbuilder扩容:
StringBuilder sb = new StringBuilder("abc");
String为什么是不可变的?
String类中使用final关键字修饰字符数组来保存字符串:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
//...
}
final关键字:被
final
关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象
如果String是引用类型,则它的值理论上可变的。所以我们不能说 final关键字修饰的数组保存字符串 是String不可变的根本原因。但是为什么还是说String不可变呢?因为:
- 保存字符串的数组被final修饰且为私有,并且String类没有提供/暴露修改这个字符串的方法。
- Sring类被final修饰导致其不能被继承,进而避免了子类破坏String不可变。