Java StringBuilder和StringBuffer源码分析

StringBuilder与StringBuffer是两个常用的操作字符串的类。大家都知道,StringBuilder是线程不安全的,而StringBuffer是线程安全的。前者是JDK1.5加入的,后者在JDK1.0就有了。下面分析一下它们的内部实现。

一、继承关系

public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence

public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence

可以看到,两个类的继承关系是一模一样的。Serializable是可以序列化的标志。CharSequence接口包含了charAt()、length() 、subSequence()、toString()这几个方法,String类也实现了这个接口。这里的重点是抽象类AbstractStringBuilder,这个类封装了StringBuilder和StringBuffer大部分操作的实现。

二、AbstractStringBuilder

1、变量及构造方法

char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
  value = new char[capacity];
}

AbstractStringBuilder内部用一个char[]数组保存字符串,可以在构造的时候指定初始容量方法。

2、扩容

public void ensureCapacity(int minimumCapacity) {
  if (minimumCapacity > 0)
    ensureCapacityInternal(minimumCapacity);
}
 private void ensureCapacityInternal(int minimumCapacity) {
  // overflow-conscious code
  if (minimumCapacity - value.length > 0)
    expandCapacity(minimumCapacity);
}
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);
}

扩容的方法最终是由expandCapacity()实现的,在这个方法中首先把容量扩大为原来的容量加2,如果此时仍小于指定的容量,那么就把新的容量设为minimumCapacity。然后判断是否溢出,如果溢出了,把容量设为Integer.MAX_VALUE。最后把value值进行拷贝,这显然是耗时操作。

3、append()方法

public AbstractStringBuilder append(String str) {
    if (str == null)
      return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
  }

append()是最常用的方法,它有很多形式的重载。上面是其中一种,用于追加字符串。如果str是null,则会调用appendNull()方法。这个方法其实是追加了'n'、'u'、'l'、'l'这几个字符。如果不是null,则首先扩容,然后调用String的getChars()方法将str追加到value末尾。最后返回对象本身,所以append()可以连续调用。

三、StringBuilder

AbstractStringBuilder已经实现了大部分需要的方法,StringBuilder和StringBuffer只需要调用即可。下面来看看StringBuilder的实现。

1、构造器

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。当然也可以指定初始容量,或者以一个已有的字符序列给StringBuilder对象赋初始值。

2、append()方法

public StringBuilder append(String str) {
  super.append(str);
  return this;
}
public StringBuilder append(CharSequence s) {
  super.append(s);
  return this;
}

append()的重载方法很多,这里随便列举了两个。显然,这里是直接调用的父类AbstractStringBuilder中的方法。

3、toString()

 public String toString() {
  // Create a copy, don't share the array
  return new String(value, 0, count);
}

toString()方法返回了一个新的String对象,与原来的对象不共享内存。其实AbstractStringBuilder中的subString()方法也是如此。

四、SringBuffer

StiringBuffer跟StringBuilder类似,只不过为了实现同步,很多方法使用lSynchronized修饰,如下面的方法:

public synchronized int length() {
    return count;
}
public synchronized StringBuffer append(String str) {
  toStringCache = null;
  super.append(str);
  return this;
}
public synchronized void setLength(int newLength) {
  toStringCache = null;
  super.setLength(newLength);
}

可以看到,方法前面确实加了Synchronized。
另外,在上面的append()以及setLength()方法里面还有个变量toStringCache。这个变量是用于最近一次toString()方法的缓存,任何时候只要StringBuffer被修改了这个变量会被赋值为null。StringBuffer的toString如下:

public synchronized String toString() {
  if (toStringCache == null) {
    toStringCache = Arrays.copyOfRange(value, 0, count);
  }
  return new String(toStringCache, true);
}

在这个方法中,如果toStringCache为null则先缓存。最终返回的String对象有点不同,这个构造方法还有个参数true。找到String的源码看一下:

 String(char[] value, boolean share) {
  // assert share : "unshared not supported";
  this.value = value;
}

原来这个构造方法构造出来的String对象并没有实际复制字符串,只是把value指向了构造参数,这是为了节省复制元素的时间。不过这个构造器是具有包访问权限,一般情况下是不能调用的。

总结

  • StringBuilder和StringBuffer都是可变字符串,前者线程不安全,后者线程安全。
  • StringBuilder和StringBuffer的大部分方法均调用父类AbstractStringBuilder的实现。其扩容机制首先是把容量变为原来容量的2倍加2。最大容量是Integer.MAX_VALUE,也就是0x7fffffff。
  • StringBuilder和StringBuffer的默认容量都是16,最好预先估计好字符串的大小避免扩容带来的时间消耗。

以上就是本文的全部内容,希望对大家学习Java中两个常用的操作字符串的类StringBuilder和StringBuffer有所帮助。

(0)

相关推荐

  • 详解Java中字符串缓冲区StringBuffer类的使用

    StringBuffer 是一个线程安全的可变的字符序列.它继承于AbstractStringBuilder,实现了CharSequence接口. StringBuilder 也是继承于AbstractStringBuilder的子类:但是,StringBuilder和StringBuffer不同,前者是非线程安全的,后者是线程安全的. StringBuffer 和 CharSequence之间的关系图如下: StringBuffer类和String一样,也用来代表字符串,只是由于StringB

  • java 中String和StringBuffer与StringBuilder的区别及使用方法

    java 中String和StringBuffer与StringBuilder的区别及使用方法 1. String 类 String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间. String a = "a"; //假设a指向地址0x0001 a = "b";//重新赋值后a指向地址0x0002,但0x0001地址中保存的"a"依旧存在,但已经不再是a所指向的,a 已经指向

  • Java 中String StringBuilder 与 StringBuffer详解及用法实例

    在Android/Java开发中,用来处理字符串常用的类有3种: String.StringBuilder.StringBuffer. 它们的异同点: 1) 都是 final 类, 都不允许被继承; 2) String 长度是不可变的, StringBuffer.StringBuilder 长度是可变的; 3) StringBuffer 是线程安全的, StringBuilder 不是线程安全的. String VS StringBuffer String 类型和StringBuffer的主要性

  • Java 中 String,StringBuffer 和 StringBuilder 的区别及用法

    1 简介 在 Java 语言中,共有 8 个基本的数据类型,分别为:byte.short.int.long.float.double.boolean 和 char,其中 char 类型用于表示单个字符,例如 a.b.c .A.B.C.& 这些大小写字母或者特殊字符等等.在实际的编程中,单个的字符并没有我们想象中用的那么频繁,反而是多个字符组成的"字符串"更为常见,但是在基本的数据类型中,并没有字符串这种数据类型.为了解决这个问题,Java 语言为我们提供了一个被 final 关

  • Java中StringBuffer和StringBuilder_动力节点Java学院整理

    下面先给大家介绍下String.StringBuffer.StringBuilder区别,具体详情如下所示: StringBuffer.StringBuilder和String一样,也用来代表字符串.String类是不可变类,任何对String的改变都 会引发新的String对象的生成:StringBuffer则是可变类,任何对它所指代的字符串的改变都不会产生新的对象.既然可变和不可变都有了,为何还有一个StringBuilder呢?相信初期的你,在进行append时,一般都会选择StringB

  • JAVA中String类与StringBuffer类的区别

    在Java中有两种字符串的操作:String类和StringBuffer类(缓冲字符串处理类). 下面先简单的说一下两者的区别. String类和StringBuffer类都提供了相应的方法实现字符串的操作,但二者略有不同. (1) String类 该类一旦产生一个字符串,其对象就不可变.String类的内容和长度是固定的.如果程序需要获得字符串的信息需要调用系统提供的各种字符串操作方法实现.虽然通过各种系统方法可以对字符串施加操作,但这并不改变对象实例本身,而是生成一个新的实例.系统为Stri

  • java中 String和StringBuffer的区别实例详解

    java中 String和StringBuffer的区别实例详解 String: 是对象不是原始类型.            为不可变对象,一旦被创建,就不能修改它的值.            对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去.            String 是final类,即不能被继承. StringBuffer: 是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象            它只能通过构造函数来建立,  

  • Java那点事——StringBuffer与StringBuilder原理与区别

    最近在找工作,考官问我一个简单的题目:"StringBuffer与StringBuilder的区别,它们的应用场景是什么?",下面小编答案分享给大家,方便以后大家学习,以此也做个备录. 其实只要找下Google大神就有答案了:StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全

  • 详细分析Java中String、StringBuffer、StringBuilder类的性能

    我们先要记住三者的特征: String 字符串常量 StringBuffer 字符串变量(线程安全) StringBuilder 字符串变量(非线程安全) 一.定义 查看API会发现,String.StringBuffer.StringBuilder都实现了 CharSequence接口,虽然它们都与字符串相关,但是其处理机制不同. String:是不可改变的量,也就是创建后就不能在修改了. StringBuffer:是一个可变字符串序列,它与String一样,在内存中保存的都是一个有序的字符串

  • Java String 和StringBuffer的详解及区别

    Java String 和StringBuffer的详解及区别 Java平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含 多个字符的字符数据.String类表示内容不可改变的字符串.而StringBuffer类表示内 容可以被修改的字符串. 当你知道字符数据要改变的时候你就可以使用StringBuffer.典型地,你可以使用StringBuffers来动态构造 字符数据.另外,String实现了equals方法,new String("abc"

随机推荐