Java中的字符串

97 阅读2分钟

环境

  • JDK1.8

01、String

String对象一旦创建就不可改变,String类被final关键字修饰,且String类的成员变量value[]也被final修饰。如下(jdk1.8)

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    private final char value[];
    //....其它源码
}

02、StringBuffer

StringBuffer创建的字符串对象是是可变的,为什么说是可变呢?

StringBuffer继承AbstractStringBuilder,AbstractStringBuilder中维护着char[]字符数组和count字符数组长度两个成员变量,且都没有通过final关键字修饰。append方法会调用AbstractStringBuilder.append方法,代码如下: AbstractStringBuilder.append(String str)

public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    // 判断长度溢出
    ensureCapacityInternal(count + len);
    // 复制要append的字符串到 value 数组中,追加到数组末尾
    str.getChars(0, len, value, count);
    // 增加数组长度
    count += len;
    return this;
}

看下 String.getChars(...) 方法源码,如下:

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
    if (srcBegin < 0) {
        throw new StringIndexOutOfBoundsException(srcBegin);
    }
    if (srcEnd > value.length) {
        throw new StringIndexOutOfBoundsException(srcEnd);
    }
    if (srcBegin > srcEnd) {
        throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
    }
    //// 通过 arraycopy 追加到目标字符数组中
    System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}

所以StringBuffer、StringBuilder的可变性是由可变字符数组char[], 数组长度count, System.arraycopy来完成的。

private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,
                newCapacity(minimumCapacity));
    }
}

值得注意的是StringBuffer是线程安全的,体现在该类的方法上有synchronized关键字修饰。

03、StringBuilder

StringBuilder创建的字符串对象也是可变的,但提供的方法是非线程安全的。适合用于单线程处理大字符串的场景。JDK8中默认使用 StringBuilder 拼接字符串,所以不用显示的使用StringBuilder了,直接用"+"也可以。

04、String.intern()

该方法会从字符串常量池中检查是否有相同值,如果有,则直接使用常量池中的字符串对象,否则就在常量池中创建一个字符串对象并返回。

应用场景:产生大量重复字符串对象的时候,可以通过该方法来节省内存空间。

总结

  • String对象是不可变的,StringBuffer、StringBuilder创建的对象是可变的;
  • StringBuffer是线程安全的,StringBuilder是非线程安全的;
  • 在出现大量重复字符串的时候,考虑一下String.intern()