//------------------------------------------------------------------------

写篇博客不easy。请尊重作者劳动成果。

转载请注明出处: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 = 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 = 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 = 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 = (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());
}
恩,确实没加,但注意到凝视没。设计者说了。加锁的操作是通过它内部调用的其他方法实现的,所以这里不是必需再进行一次加锁(锁须要浪费资源)。

剩下的代码都没啥说的了。除了加了synchronizedkeyword修饰,跟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。

【源代码】StringBuilder和StringBuffer震源深度分析的更多相关文章

  1. Java StringBuilder 和 StringBuffer 源码分析

    简介 StringBuilder与StringBuffer是两个常用的操作字符串的类.大家都知道,StringBuilder是线程不安全的,而StringBuffer是线程安全的.前者是JDK1.5加 ...

  2. StringBuilder、StringBuffer分析比较

    StringBuilder.StringBuffer源码分析 StringBuilder源码分析 类结构 public final class StringBuilder extends Abstra ...

  3. StringBuilder和StringBuffer的区别

    Java中StringBuilder和StringBuffer的区别分析 StringBUilder是线程不安全的(线程同步访问的时候会出问题),但是效率相对较高. (String类型使用加号进行拼接 ...

  4. String、StringBuilder、 StringBuffer 深入分析 源代码解析

    java学习有一段时间了.但学习的东西都是框架等东西,java基础知识有点遗忘.所以重温一下java基础知识.写写文章里面有错的希望大家指正共同进步~~ 一.String 大家常常会说使用" ...

  5. 探秘Java中的String、StringBuilder以及StringBuffer

    探秘Java中String.StringBuilder以及StringBuffer 相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问 到的地方,今天就来和大家一起学习 ...

  6. 探秘Java中String、StringBuilder以及StringBuffer

    探秘Java中String.StringBuilder以及StringBuffer 相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问 到的地方,今天就来和大家一起学习 ...

  7. Java中String、StringBuilder以及StringBuffer

    原文出处: 海子 相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问到的地方,今天就来和大家一起学习一下String.StringBuilder和StringBuffe ...

  8. 转发: 探秘Java中的String、StringBuilder以及StringBuffer

    原文地址 探秘Java中String.StringBuilder以及StringBuffer 相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问到的地方,今天就来和大家 ...

  9. 探秘Java中的String、StringBuilder以及StringBuffer(转载)

    探秘Java中String.StringBuilder以及StringBuffer 相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问到的地方,今天就来和大家一起学习一 ...

随机推荐

  1. 冒泡排序----java实现

    冒泡排序思路:第1次:顺序比较从第0个到第len个(相邻两个)元素并把大的放后面,第一次进行完后,最大                                         的元素会在最后: ...

  2. Linux下/proc目录简介(转)

    1. /proc目录Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构.改变内核设置的机制.proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文 ...

  3. 网站通常使用一些javascript包裹 简化电话

    //对于Web地址参数 //前面加"=="进行标识,否则直接返回 //解码时依据是否含有"=="标识来决定是否要解码 var base64EncodeChars ...

  4. Excel一项研究指出色彩

    Excel一项研究指出色彩 从Office2007跳到了2013版本号,尽管有着种种不适,但应该承认Excel2013版本号在图表和色彩上更加人性化和"专业"了一些. 但假设须要更 ...

  5. Javascript作用域问题的构造函数的变量

    构造函数new对于使用.代表创建对象.此外,它可以被用作普通的函数调用,因为它也是一个功能. function Person(name) { this.name=name; } Person(12); ...

  6. 我有DIY一Android遥控-所有开源

    我有DIY一Android遥控-所有开源 1.试用 记得宋宝华在「设备驱动开发具体解释」提出一个这种理论「软件和硬件互相渗透对方的领地」,这次证明还是确实是这样,使用上层APP软件加上简单的更为简单的 ...

  7. 【转】Android 4.3源码下载及问题解决

    [html] view plaincopy 1 2 3 4 5 6 7 8 9 10 11 jianguoliao@jianguoliao-Lenovo-IdeaPad-Y470:~$ cat /et ...

  8. jedis异常:NoSuchElementException: Timeout waiting for idle object

    项目发现网络环境:java.util.NoSuchElementException: Timeout waiting for idle object 问题原因:不应该在try中释放资源.而应该在fin ...

  9. Node.js v0.10.31API手冊-事件

    Node.js v0.10.31API手冊-文件夹 Events(事件) Node里面的很多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, 一个fs.readStrea ...

  10. android 电平信号状态识别View平局

    1.前言 级信号状态View在今天的Android系统是常见.状态的图标就很的经典,有几种状态,到了快没电的时候有些还会闪烁提示用户充电:还有的就是一些地图App的GPS信号强度的提示.Wifi信号强 ...