首页 > 代码库 > 【源码】StringBuilder和StringBuffer源码深度剖析
【源码】StringBuilder和StringBuffer源码深度剖析
//------------------------------------------------------------------------
写篇博客不容易,请尊重作者劳动成果。转载请注明出处:http://blog.csdn.net/chdjj
//------------------------------------------------------------------------
我觉得要通过源码研究一个类,应该先从整体上了解这个类,比如说这个类的继承体系,有哪些超类,继承了那些接口,提供了什么样的方法。然后,我们再去源码中了解具体的实现过程,这样才不会乱~
注:以下源码基于jdk1.7.0_11
我们来看看StringBuilder和StringBuffer的继承关系:
public final class StringBuilder//1.5开始 extends AbstractStringBuilder implements java.io.Serializable, CharSequence public final class StringBuffer//1.0就有了 extends AbstractStringBuilder implements java.io.Serializable, CharSequence
惊人的一致,两个类都继承了AbstractStringBuilder,并且实现CharSequence和Serializable接口,Serializable接口大家都熟悉,是一个序列化的标志。那么我们先来看CharSquence接口:
package java.lang; /** * @author Mike McCloskey * @since 1.4 * @spec JSR-51 */ public interface CharSequence { int length(); char charAt(int index); CharSequence subSequence(int start, int end); public String toString(); }
只有四个方法,这个接口为不同的字符序列类提供了统一的规范,看到这个类有个toString的方法,说明它强制子类去实现toString,而不使用默认的toString。其他三个方法也比较好理解,甚至大家在String中还经常使用这些方法,这里就不提了。
分析完了Charsequence接口,下面再看看AbstractStringBuilder类吧,听名字就知道是个抽象类。这个类是到1.5才有的,很显然是由于增加了StringBuilder,设计者觉着可以将StringBuilder和StringBuffer进行泛化,抽取共同部分,这体现了面向对象的设计理念。
abstract class AbstractStringBuilder implements Appendable, CharSequence
AbstractStringBuilder又实现了CharSequence和Appendable接口,Appendable看名字就知道肯定跟StringBuilder和StringBuffer的可变性有关,我们果断看看Appendable接口:
package java.lang; import java.io.IOException; public interface Appendable { Appendable append(CharSequence csq) throws IOException; Appendable append(CharSequence csq, int start, int end) throws IOException; Appendable append(char c) throws IOException; }
果然,这个接口提供了append方法的不同重载形式,返回值均为自己(注:看到这里,我想到了一句话,抽象类是对类进行抽象,而接口是对行为进行抽象,想想确实如此呢),既然AbstractStringBuilder实现了此接口,必然提供了实现,下面我们回到AbstractStringBuilder类中看看这个类都干了啥。
先看成员变量:
/** * The value is used for character storage. */ char[] value;//用于存放字符的数组 /** * The count is the number of characters used. */ int count;//当前字符总数
注意,这里的value数组并没有声明为final(String类的value数组是final的),说明此数组可改变。
再看构造器:
AbstractStringBuilder() {} AbstractStringBuilder(int capacity) { value = http://www.mamicode.com/new char[capacity];>一个无参构造器,一个构造器提供char数组初始容量,value数组根据此容量创建对象。接下来看几个重要的方法。首先是扩容的方法:public void ensureCapacity(int minimumCapacity) { if (minimumCapacity > 0) ensureCapacityInternal(minimumCapacity); } /** * This method has the same contract as ensureCapacity, but is * never synchronized. */ private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity); } /** * This implements the expansion semantics of ensureCapacity with no * size check or synchronization. */ void expandCapacity(int minimumCapacity) { int newCapacity = value.length * 2 + 2; if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity; if (newCapacity < 0) { if (minimumCapacity < 0) // overflow throw new OutOfMemoryError(); newCapacity = Integer.MAX_VALUE; } value = http://www.mamicode.com/Arrays.copyOf(value, newCapacity);>ensureCapacity方法确保当前字符数组的容量最小为minimumCapacity,首先判断这个参数是否为负,若是,则返回,否则调用ensureCapacityInternal方法,这个方法内部将判断当前容量是否小于minimumCapacity,若是,则进行扩容,否则返回。扩容是通过调用expandCapacity方法来实现的,这个方法内部实现逻辑是这样的:首先试着将当前数组容量扩充为原数组容量的2倍加上2,如果这个新容量仍然小于最小值(minimumCapacity),那么就将新容量定为(minimumCapacity),最后判断是否溢出,若溢出,则将容量定为整型的最大值0x7fffffff。容量定好之后,进行一次数组拷贝。通过这一系列步骤,应该对我们有所启发,在创建StringBuilder或StringBuffer时,尽量估算下串的长度,给一个合理的初始值,避免多次扩容带来的效率问题。接下来看append方法(重载太多,这里只列举1个):public AbstractStringBuilder append(String str) { if (str == null) str = "null"; int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; }上面这个append方法接受一个String类型的参数,也是我们最常使用的重载形式.内部逻辑比较简单,首先进行参数判断(我们写代码的时候也应该时刻注意代码的鲁棒性,注意参数的取值),若参数为空(null),则将null这几个字符作为参数,接下来判断是否需要扩容,完了之后进行一次复制,最后更新count。还有个方法叫trimToSize,这个方法可以减少内存空间的使用,内部会进行数组复制,释放那些尚未使用的空间:public void trimToSize() { if (count < value.length) { value = http://www.mamicode.com/Arrays.copyOf(value, count);>至此,AbstractStringBuilder分析完毕。好吧,现在我们可以分析StringBuilder和StringBuffer了!先来看看这个StringBuilder:首先是构造器:public StringBuilder() { super(16); } public StringBuilder(int capacity) { super(capacity); } public StringBuilder(String str) { super(str.length() + 16); append(str); } public StringBuilder(CharSequence seq) { this(seq.length() + 16); append(seq); }通过这个构造器我们知道了StringBuilder的默认大小为16,这个很关键哦,大家应该能够记住,如果能在面试中说出来,那也是极好的~构造器除了有默认容量,也可以手动设置容量,为避免扩容,强烈建议大家估摸下串的大致长度~接下来是append方法,我们推测应该调用的是AbstractStringBuilder中的append:public StringBuilder append(Object obj) { return append(String.valueOf(obj)); } public StringBuilder append(String str) { super.append(str); return this; }事实上也正是这样。再来看下这个toString方法:public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }注意哦,这里返回的是一个新的字符串对象!最后看下readObject和writeObject这两个方法,这两个方法都是私有的,方法的作用应该跟序列化有关。private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); s.writeInt(count); s.writeObject(value); } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); count = s.readInt(); value = http://www.mamicode.com/(char[]) s.readObject();>到这,StringBuilder分析完毕,可以看出,StringBuilder中并没有什么复杂的逻辑,实现代码主要在AbstractStringBuilder中。下面该分析StringBuffer了,大家都知道,StringBuffer和StringBuilder的最大区别就是StringBuffer线程安全,那么可想而知,StringBuffer的方法应该加锁了,带着这个猜想,打开StringBuffer源码:构造器就略过了,因为StringBuffer跟StringBuilder完全一样,默认容量也是16.下面随便来几个方法:public synchronized int length() { return count; } public synchronized void trimToSize() { super.trimToSize(); } public synchronized StringBuffer append(String str) { super.append(str); return this; }看,都加锁了吧。你可能看到这个方法没加锁:public StringBuffer append(CharSequence s) { // Note, synchronization achieved via other invocations if (s == null) s = "null"; if (s instanceof String) return this.append((String)s); if (s instanceof StringBuffer) return this.append((StringBuffer)s); return this.append(s, 0, s.length()); }恩,确实没加,但注意到注释没,设计者说了,加锁的操作是通过它内部调用的其它方法实现的,所以这里没必要再进行一次加锁(锁需要浪费资源)。剩下的代码都没啥说的了,除了加了synchronized关键字修饰,跟StringBuilder是一样一样的。分析到此完毕,下面做个总结:1.StringBuilder是jdk1.5引进的,而StringBuffer在1.0就有了;2.StringBuilder和StringBuffer都是可变的字符串,可以通过append或者insert等方法修改串的内容;3.StringBuffer是线程安全的而StringBuilder不是,因而在多线程的环境下优先使用StringBuffer,而其他情况下推荐使用StringBuilder,因为它更快;4.StringBuilder和StringBuffer都继承自AbstractStringBuilder类,AbStractStringBuilder主要实现了扩容、append、insert方法,StrngBuilder和StringBuffer的相关方法都直接调用的父类。5.StringBuilder和StringBuffer的初始容量都是16,程序员尽量手动设置初始值,以避免多次扩容所带来的性能问题;6.StringBuilder和StringBuffer的扩容机制是这样的:首先试着将当前数组容量扩充为原数组容量的2倍加上2,如果这个新容量仍然小于预定的最小值(minimumCapacity),那么就将新容量定为(minimumCapacity),最后判断是否溢出,若溢出,则将容量定为整型的最大值0x7fffffff。
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。