您的位置:首页 > 编程语言 > Java开发

Java 7之基础类型第6篇 - Java可变字符串类型

2013-12-31 20:59 1131 查看
在实际的项目开发过程中,可能我们更多时候是在操作可变的字符串,例如,动态的拼接SQL语句。
之所以叫StringBuilder和StringBuffer为可变字符串是因为:
(1)提供了大量改变原字符串的操作,如append()方法。而改变是在原来字符串的基础上修改的,并不像String中的一些方法,通过new String()来返回一个新的字符串。
(2)字符串会自动扩容,不需要人为的进行操作,而String本身是不进行扩容的。
下面来详细了解一下这两个类的实现。首先来看一下AbstractStringBuilder类,

abstract class AbstractStringBuilder implements Appendable, CharSequence {
	// 基本的属性
    char[] value;
    int count;    // The count is the number of characters used.
    // 构造函数
    AbstractStringBuilder() {   }
    
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
    // 扩容
    public int capacity() {
        return value.length;
    }

    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);
    }

    /**
     * This implements the expansion semantics of ensureCapacity with no
     * size check or synchronization.
     */
    void expandCapacity(int minimumCapacity) { // 要保证value字符数组足够大
        int newCapacity = value.length * 2 + 2;// 扩展为原来长度的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);// 在扩容的时候都会进行原数据的复制
    }
}
如上定义了一个字符数组,并没有加任何限制。还提供了扩容的方法和几个默认的构造方法。由于类继承了Appendable接口,而这个接口主要是定义了一类方法 - append()方法,在这个抽象类中都进行了实现:
public AbstractStringBuilder append(CharSequence s, int start, int end) {
        if (s == null)
            s = "null";
        if ((start < 0) || (start > end) || (end > s.length()))
            throw new IndexOutOfBoundsException("start " + start + ", end " + end + ", s.length() "  + s.length());
        int len = end - start;// 需要复制的字符个数
        ensureCapacityInternal(count + len); // 确保value字符数组足够大
        for (int i = start, j = count; i < end; i++, j++) // 将字符串中包含的部分字符添加到value字符数组中
            value[j] = s.charAt(i);
        count += len;
        return this;
    }
有许多的append()方法,处理方法都非常类似,有兴趣的可以自己去看。append()方法只能在末尾进行追加,如果要对字符串中间进行插入,则可以使用insert()方法,举个例子:

public AbstractStringBuilder insert(int offset, String str) {
        if ((offset < 0) || (offset > length()))
            throw new StringIndexOutOfBoundsException(offset);
        if (str == null)
            str = "null";
        int len = str.length();
        ensureCapacityInternal(count + len);
        System.arraycopy(value, offset, value, offset + len, count - offset);// 从value中的指定位置向后腾出len个位置
        str.getChars(value, offset); // 将str中包含的字符插入value的指定位置
        count += len;
        return this;
    }
可以看到,都会进行自动扩容,然后将str字符串中的字符插入到value中的指定位置。由于会自动扩容,所以在使用可变的字符串时,最好指定足够的容量,以防止进行扩容。因为底层需要进行数组的拷贝。
测试程序如下:
StringBuilder str =new StringBuilder("abc");
str.insert(1, "xx");
System.out.println(str.toString());// axxbc

StringBuilder和StringBuffer类都实现了这个抽象类,只是StringBuffer类中公开的方法都加上了synchronized关键字进行同步,也是与StringBuilder的最大不同。

CharSequence是个接口,表示字符序列,而String、StringBuilder和StringBuffer都实现这个接口,底层都是通过数组来实现的。

String、StringBuilder、StringBuffer的异同点结合之前写的博文,我们对这三个常用的类的异同点进行分析:
异同点:
String的对象是不可变的;而StringBuilder和StringBuffer是可变的。
StringBuilder不是线程安全的;而StringBuffer是线程安全的
String中的offset,value,count都是被final修饰的不可修改的;而StringBuffer和StringBuilder中的value,count都是继承自AbstractStringBuilder类的,没有被final修饰,说明他们在运行期间是可修改的,而且没有offset变量。
相同点:
三个类都是被final修饰的,是不可被继承的。且都能表示表示字符序列(源代码中可以看出都继承了CharSequence.java类 )
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐