在java中,有4个类是对字符进行操作的,Character、String、StringBuilder 和 StringBuffer。
简单对比:
Character : 是对单个字符进行操作的。(JDK 1.0)
String : 是对一个字符串进行操作,而这个类是不可变类。(JDK 1.0)
StringBuilder :是对字符串进行操作,是线程不安全的可变类。(JDK 1.5)
StringBuffer :是对字符串进行操作,是线程安全的可变类。(JDK 1.0)
首先分别对 String、StringBuilder 和 StringBuffer 类进行详细介绍。
String 类 :
String 类为操作字符串的不可变类,我们可以查看源代码,看下它底层是怎么定义的。
String 中定义了一个私有的成员常量字符串组,我们知道,修饰符 final 用修饰常量是不可变的,所以如果要给一个String类的对象重新赋值时,它将重新new一个对象再去重新覆盖栈中的字符串变量。
具体 String 在new一个对象JVM的处理细节和在面试中可能会问到的关于String的问题,下周进行更新。
StringBuider 和 StringBuffer类 :
StringBuider 类 和 stringBuffer 类 是处理字符串的可变类。对于为什么可变,我们可以查看它的源代码进行深入了解
首先观察这两个类的构造器,他们两的都去调用了父类的构造方法。我们再分别看下他们都继承了那些类,调用了谁的构造方法。
这是我们发现 StringBuilder 和 StringBuffer 继承同一个抽象类 AbstractStringBuilder 。我们可以点进去看下这个 AbstractStringBuilder 类到低是怎么实现的,其构造器是怎么定义的。
我们可以看到 AbstractStringBuilder 类有一个有参构造器, StringBuilder 和 StringBuffer 就调用的是它。其实现就是将new一个长度为 copacity 的字符数组,然后赋给 成员变量 value。
因为 StrigBuildner 和 StringBuffer 调用其父类构造器时传过来的值是16,所以说 StringBuilder 和 StringBuffer 当不去设置长度时,长度默认为16。
因为 AbstractStringBuilder 的成员变量 value 是一个字符串数组,非常量,所以是可变的。我们可以看它的 append方法去仔细看下它到底是怎么实现可变的。(由于调用关系过复杂,下面图片删除了部分代码)
在追加时,我们发现,如果追加后的字符串超过了目前 value 定义的最大长度,则就会重新 new 一个字符数组,再将值拷贝过去。再重新设置 count ;
至于为什么说 StrigBuildner 线程不安全,StringBuffer 线程安全 ,我们也可以看下源代码。(例如 append方法)
我们可以看到 StringBuffer 中的方法都是使用的 synchronized 修饰为同步方法,而StringBuilder没有被修饰,所以当在多线程程序中 StringBuffer 还是比较安全的,但性能较低。
总结:
StringBuilder 和 StringBuffer 对比:
1. StringBuffer 支持并发操作,线程安全的,适合在多线程中使用。
2. StringBuilder 不支持并发操作,线程不安全,不适合多线程中使用。
3. StringBuilder 类虽然线程不安全,但在单线程中性能比StringBuffer性能高。
4. 执行速度 StringBuilder > StringBuffer 。
5. 相同点:StringBuilder 和 StringBuffer 有公共的父类,AbstractStringBuilder (抽象类)。
String 类 和 StringBuffer 对比:
1. String 类是不可变类,任何对 String 的改变都会引发新的String 对象的生成。StringBuffer 是可变类,任何对它的改变不会引发新对象的生成,如果长度越界,就会重新申请更长的字符数组。
2. 执行速度 : StringBuffer > String
使用策略:
1. 基本原则:如果要操作少量的数据,用String ;单线程操作大量数据,用StringBuilder ;多线程操作大量数据,用StringBuffer。
2. 不要使用String类的"+"来进行频繁的拼接,因为那样的性能极差的,应该使用StringBuffer或StringBuilder类,这在Java的优化上是一条比较重要的原则。
3. 为了获得更好的性能,在构造 StringBuffer 或 StringBuilder 时应尽可能指定它们的容量。如果你操作的字符串长度(length)不超过 16 个字符就不用了,当不指定容量(capacity)时默认构造一个容量为16的对象。不指定容量会显著降低性能(扩容每次加16)。
4. StringBuilder一般使用在方法内部来完成类似"+"功能,因为是线程不安全的,所以用完以后可以丢弃。StringBuffer主要用在全局变量中。
5. 相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。而在现实的模块化编程中,负责某一模块的程序员不一定能清晰地判断该模块是否会放入多线程的环境中运行,因此:除非确定系统的瓶颈是在 StringBuffer 上,并且确定你的模块不会运行在多线程模式下,才可以采用StringBuilder;否则还是用StringBuffer。