从源代码的角度聊聊java中StringBuffer、StringBuilder、String中的字符串拼接

简介:

长久以来,我们被教导字符串的连接最好用StringBuffer、StringBuilder,但是我们却不知道这两者之间的区别.跟字符串相关的一些方法中总是有CharSequence、StringBuffer、StringBuilder、String,他们之间到底有什么联系呢?

1、从类的定义看CharSequence、StringBuffer、StringBuilder、String的关系

下面先贴上这四者的定义(来自JDK1.6)

CharSequence是一个定义字符串操作的接口,StringBuffer、StringBuilder、String中都实现了这个接口.

复制代码
//CharSequence定义
public interface CharSequence


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

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

//String定义
public final class String
    implements
java.io.Serializable, Comparable<String>, CharSequence
复制代码

String 是java中的字符串,它继承于CharSequence。 
String类所包含的API接口非常多。为了便于今后的使用,我对String的API进行了分类,并都给出的演示程序。

String 和 CharSequence 关系 
String 继承于CharSequence,也就是说String也是CharSequence类型。 
CharSequence是一个接口,它只包括length(), charAt(int index), subSequence(int start, int end)这几个API接口。除了String实现了CharSequence之外,StringBuffer和StringBuilder也实现了CharSequence接口。 
       也就是说,CharSequence其实也就是定义了字符串操作的接口,其他具体的实现是由String、StringBuilder、StringBuffer完成的,String、StringBuilder、StringBuffer都可以转化为CharSequence类型。

StringBuilder 和 StringBuffer 的区别

StringBuilder 和 StringBuffer都是可变的字符序列。它们都继承于AbstractStringBuilder,实现了CharSequence接口。 
但是,StringBuilder是非线程安全的,而StringBuffer是线程安全的。

它们之间的关系图如下: 

 

 

2、从构造函数到具体的字符串拼接操作看看String、StringBuffer、StringBuilder的区别

下面我们来分析一下String、StringBuffer、StringBuilder具体的构造函数,了解他们是怎么构造出来的,再看看具体的字符串连接操作。

(1)String

String的构造函数(几个常见的构造函数)

复制代码
public String() {
        this.offset = 0;
        this.count = 0;
        this.value = new char[0];
    }

    /**
     * Initializes a newly created {@code String} object so that it represents
     * the same sequence of characters as the argument; in other words, the
     * newly created string is a copy of the argument string. Unless an
     * explicit copy of {@code original} is needed, use of this constructor is
     * unnecessary since Strings are immutable.
     *
     * @param  original
     *         A {@code String}
     */
    public String(String original) {
        int size = original.count;
        char[] originalValue = original.value;
        char[] v;

        if (originalValue.length > size) {
            // The array representing the String is bigger than the new
            // String itself.  Perhaps this constructor is being called
            // in order to trim the baggage, so make a copy of the array.
            int off = original.offset;
            v = Arrays.copyOfRange(originalValue, off, off + size);
        } else {
            // The array representing the String is the same
            // size as the String, so no point in making a copy.
            v = originalValue;
        }

        this.offset = 0;
        this.count = size;
        this.value = v;
    }

    /**
     * Allocates a new {@code String} so that it represents the sequence of
     * characters currently contained in the character array argument. The
     * contents of the character array are copied; subsequent modification of
     * the character array does not affect the newly created string.
     *
     * @param  value
     *         The initial value of the string
     */
    public String(char[] value) {
        this.offset = 0;
        this.count = value.length;
        this.value = StringValue.from(value);
    }
复制代码

再看看String中具体的Concat函数

复制代码
public String concat(String str) {
        int otherLen = str.length();

        if (otherLen == 0) {
            return this;
        }

        char[] buf = new char[count + otherLen];
        getChars(0, count, buf, 0);
        str.getChars(0, otherLen, buf, count);

        return new String(0, count + otherLen, buf);
    }
复制代码

从Concat函数中,我们可以知道在对字符串使用concat操作后,具体的操作new出一个等同于两个字符串连接总长度的新的char数组,然后将两个字符串复制到新的char数组中,然后返回一个新的String对象。

(2)StringBuilder

StringBuilder常见构造函数

复制代码
public StringBuffer() {
    super(16);
    }


    public StringBuffer(int capacity) {
    super(capacity);
    }
复制代码

从StringBuilder的构造函数中,我们可以看见StringBuilder直接调用父类(AbstractStringBuilder)的构造函数,我们再看看AbstractStringBuilder的构造函数

复制代码
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    final static int[] sizeTable = {
            9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999,
            Integer.MAX_VALUE
        };

    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;

    /**
     * This no-arg constructor is necessary for serialization of subclasses.
     */
    AbstractStringBuilder() {
    }

    /**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
//其他的一些逻辑
}
复制代码

从AbstractStringBuilder的构造函数中,我们可以看出StringBuilder中存储字符串其实用的是一个char数组,capacity其实就是指定这个char数组的大小。

下面我们再从StringBuilder中的append函数看看他具体是怎么做的(以 append(String str) 为例看看)。

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

又是直接调用父类(AbstractStringBuilder)的append方法,再跟到父类中去看看。

复制代码
/**
     *  value 用来存储字符串.
     */
    char value[];

    /** 
     * 有效字符串的数目.
     */
    int count;


    public AbstractStringBuilder append(String str) {
        if (str == null) {
            str = "null";
        }

        int len = str.length();
        if (len == 0) {
            return this;
        }

        int newCount = count + len;
        if (newCount > value.length) {
            expandCapacity(newCount);
        }
       //getChars将字符串复制到指定的位置
        str.getChars(0, len, value, count);
        count = newCount;
        return this;
    }
复制代码

上面的逻辑还是比较简单的,在append(str)函数调用的时候,首先会判断原来用于存储字符串的values的字符串数组有没有足够的大小来存储将要新添加入StringBuilder的字符串。如果不够用,那么就调用expandCapacity(int minimumCapacity)让容量翻两倍(一般是扩大两倍,特殊情况见代码),如果够用,那么就直接添加进去。

复制代码
/**
     * This implements the expansion semantics of ensureCapacity with no
     * size check or synchronization.
     */
    void expandCapacity(int minimumCapacity) {
        int newCapacity = (value.length + 1) * 2;

        if (newCapacity < 0) {
            newCapacity = Integer.MAX_VALUE;
        } else if (minimumCapacity > newCapacity) {
            newCapacity = minimumCapacity;
        }

        value = Arrays.copyOf(value, newCapacity);
    }
复制代码

(3)StringBuffer

StringBuffer的构造函数

复制代码
/**
     * Constructs a string buffer with no characters in it and an 
     * initial capacity of 16 characters. 
     */
    public StringBuffer() {
    super(16);
    }

    /**
     * Constructs a string buffer with no characters in it and 
     * the specified initial capacity. 
     *
     * @param      capacity  the initial capacity.
     * @exception  NegativeArraySizeException  if the <code>capacity</code>
     *               argument is less than <code>0</code>.
     */
    public StringBuffer(int capacity) {
    super(capacity);
    }
复制代码

StringBuffer也是直接调用父类(AbstractStringBuilder)的构造函数,那么我们从上面的分析中,就可以知道StringBuffer其实也是利用char[]类型的数组来保存字符串数组的。

再看看StringBuffer的append函数

public synchronized StringBuffer append(String str) {
    super.append(str);
        return this;
    }

还是调用父类的append函数,但是在这里有值得注意的地方,StringBuffer的append函数有一个synchronized标识符,也就是说StringBuffer中的append函数是线程安全的,通过继续查阅其他StringBuffer中的函数,我们也可以发现他们有synchronized标识符,这就不难理解为什么StringBuffer是线程安全的,但是很明显加上线程控制会拖慢程序运行的速度,所以如果不需要线程控制,那么最好就用StringBuilder。

复制代码
//下面只是节选一些StringBuffer中的函数
synchronized StringBuffer     append(char ch)
synchronized StringBuffer     append(char[] chars)
synchronized StringBuffer     append(char[] chars, int start, int length)
synchronized StringBuffer     append(Object obj)
synchronized StringBuffer     append(String string)
synchronized StringBuffer     append(StringBuffer sb)
synchronized StringBuffer     append(CharSequence s)
synchronized StringBuffer     append(CharSequence s, int start, int end)
synchronized StringBuffer     insert(int index, char ch)
synchronized StringBuffer     insert(int index, char[] chars)
synchronized StringBuffer     insert(int index, char[] chars, int start, int length)
synchronized StringBuffer     insert(int index, String string)
StringBuffer     insert(int index, Object obj)
复制代码

 

后记:

可能很多同学会想了解String中的+和StringBuilder.append的效率,以及纠结要用哪个。我在网上发现有人已经写了一篇文章,分享给大家在Java中连接字符串时是使用+号还是使用StringBuilder

 

参考链接

在Java中连接字符串时是使用+号还是使用StringBuilder

String详解, String和CharSequence区别, StringBuilder和StringBuffer的区别 (String系列之1)


作者:kissazi2 
出处:http://www.cnblogs.com/kissazi2/ 
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载:http://www.cnblogs.com/kissazi2/p/3648671.html

目录
相关文章
|
2月前
|
自然语言处理 Java Apache
在Java中将String字符串转换为算术表达式并计算
具体的实现逻辑需要填写在 `Tokenizer`和 `ExpressionParser`类中,这里只提供了大概的框架。在实际实现时 `Tokenizer`应该提供分词逻辑,把输入的字符串转换成Token序列。而 `ExpressionParser`应当通过递归下降的方式依次解析
181 14
|
3月前
|
存储 安全 Java
String StringBuffer StringBuilder 区别详解与对比分析
本文详细解析了Java中String、StringBuffer和StringBuilder的区别,从可变性、线程安全性和性能三个方面进行对比,并结合具体应用场景分析了三者的适用范围。通过性能测试示例展示了它们在字符串拼接时的效率差异,同时提供了实际代码案例帮助理解。总结指出,String适合少量操作或线程安全场景,StringBuffer适用于多线程环境,而StringBuilder则在单线程下性能最优。开发者应根据需求选择合适的类以优化程序性能。文末还附有相关面试资料供参考。
553 2
|
6月前
|
缓存 安全 Java
《从头开始学java,一天一个知识点》之:字符串处理:String类的核心API
🌱 **《字符串处理:String类的核心API》一分钟速通!** 本文快速介绍Java中String类的3个高频API:`substring`、`indexOf`和`split`,并通过代码示例展示其用法。重点提示:`substring`的结束索引不包含该位置,`split`支持正则表达式。进一步探讨了String不可变性的高效设计原理及企业级编码规范,如避免使用`new String()`、拼接时使用`StringBuilder`等。最后通过互动解密游戏帮助读者巩固知识。 (上一篇:《多维数组与常见操作》 | 下一篇预告:《输入与输出:Scanner与System类》)
138 11
|
6月前
|
Java
课时14:Java数据类型划分(初见String类)
课时14介绍Java数据类型,重点初见String类。通过三个范例讲解:观察String型变量、&quot;+&quot;操作符的使用问题及转义字符的应用。String不是基本数据类型而是引用类型,但使用方式类似基本类型。课程涵盖字符串连接、数学运算与字符串混合使用时的注意事项以及常用转义字符的用法。
142 9
|
9月前
|
存储 JavaScript Java
Java 中的 String Pool 简介
本文介绍了 Java 中 String 对象及其存储机制 String Pool 的基本概念,包括字符串引用、构造方法中的内存分配、字符串文字与对象的区别、手工引用、垃圾清理、性能优化,以及 Java 9 中的压缩字符串特性。文章详细解析了 String 对象的初始化、内存使用及优化方法,帮助开发者更好地理解和使用 Java 中的字符串。
153 2
Java 中的 String Pool 简介
|
API C# 开发者
WPF图形绘制大师指南:GDI+与Direct2D完美融合,带你玩转高性能图形处理秘籍!
【8月更文挑战第31天】GDI+与Direct2D的结合为WPF图形绘制提供了强大的工具集。通过合理地使用这两种技术,开发者可以创造出性能优异且视觉效果丰富的WPF应用程序。在实际应用中,开发者应根据项目需求和技术背景,权衡利弊,选择最合适的技术方案。
821 1
|
安全 Java
【Java基础面试二十七】、说一说StringBuffer和StringBuilder有什么区别
这篇文章介绍了Java中StringBuffer和StringBuilder的区别:StringBuffer是线程安全的,而StringBuilder是非线程安全的,因此在单线程环境下优先推荐使用StringBuilder以获得更好的性能。
|
安全 Java API
Java系类 之 String、StringBuffer和StringBuilder类的区别
这篇文章讨论了Java中`String`、`StringBuffer`和`StringBuilder`三个类的区别,其中`String`是不可变的,而`StringBuffer`是线程安全的可变字符串类,`StringBuilder`是非线程安全的可变字符串类,通常在单线程环境下性能更优。
Java系类 之 String、StringBuffer和StringBuilder类的区别