JDK源码解析基础篇-String、StringBuilder、StringBuffer

首先向搞懂常量池的知识点:触摸Java常量池 常量池技术
java.lang包的最后一篇基础篇。搞完这篇就开始集合框架和并发包等内容。Sting、StirngBuilder、StringBuffer的内容很早之前写过 String、StringBuffer和StringBuilder的区别和应用场景 ,但写的太简单了。这次再重新梳理一下这部分内容,留作以后复习。
在 java 语言中, 用来处理字符串的的类常用的有 3 个: String、StringBuffer、StringBuilder。
它们的异同点:
1) 都是 final 类, 都不允许被继承;
2) String 长度是不可变的(内部实现是:private final char value[];), StringBuffer、StringBuilder 长度是可变的(内部也是利用char[]存储实现的);
3) StringBuffer 是线程安全的(通过在方法前面加了synchronized关键字实现的线程同步), StringBuilder 不是线程安全的。

String

String 字符串是常量, 它们的值在创建之后不能够被更改,它在jdk1.7之前是存储在方法区的常量池中的,jdk1.8实现了去永久代,原先的永久代信息放入了元数据区。

String str1 = "abc";
String str2 = new String("cde");

上边的两种创建方式,其中第1种是在常量池存储了字符对象char[]a,b,c,然后str1指向了此常量。第二种方式相当于创建了两个string对象,一个是cde本身,另一种是以new关键字在堆中开辟的内存空间。
当利用连接符+来改变字符串常量时,实际上是jdk1.5后jvm利用了StirngBuilder来实现的。当进行下边的代码时:

String str1 = "abc";
str1 += "cde";

jvm是采用下图中的方式实现的:
JDK源码解析基础篇-String、StringBuilder、StringBuffer_第1张图片
这实际上式jvm对+操作符的重载优化,但是这也有效率问题,当有多个连接符+时,会创建多个StringBuilder。所以对于可变的字符串,一般要采用StringBuilder和StringBuffer来实现。

String方法
string实现的方法有:implements java.io.Serializable, Comparable, CharSequence ,表明它是可序列化,以及重写了compareTo方法。
首先来看它的构造方法:
JDK源码解析基础篇-String、StringBuilder、StringBuffer_第2张图片
可以看到,我们可以利用char[],byte[]等来创建字符串对象。

由于String对象的内部是利用char数组来存储的,所以很多方法如length(),isEmpty(),charAt(),equals()等方法都是通过操作char数组来实现的,如:

    public char charAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }

startsWith(prefix) 测试字符串是否是以指定的前缀 prefix 开始, endsWith(suffix) 测试字符串是否是以指定的后缀 suffix 结束
这里写图片描述

String重写了equals方法和hashCode方法,equals方法比较的是字符对象是否一一相等,其中hashCode方法为:

    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

它是针对每一个字符来操作的。为什么选31,可以查看此解释为什么string类的hashCode方法选31作为31位数乘因子 。

indexOf 用于返回指定的子字符串在主字符串中第一次出现处的索引值; lastIndexOf 用于返回指定的子字符串在主字符串中最后一次出现处的索引值。这里并不是采用的经典的字符串匹配算法KMP算法,而是采用的暴力匹配方法。其原因可以查看为什么java String.contains 没有使用类似KMP字符串匹配算法进行优化? 。

subString方法:

    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }

string还提供了很多替换和匹配算法:
JDK源码解析基础篇-String、StringBuilder、StringBuffer_第3张图片
它是利用正则来实现的,比如:

    public String replaceAll(String regex, String replacement) {
        return Pattern.compile(regex).matcher(this).replaceAll(replacement);
    }

另外,string类还提供了例如:转换大小写,得到char[]数组,getByte方法,valueOf方法。trim()方法,intern方法(此方法与jvm知识有关,后边要看一下)等。字符串操作时我们程序中应用非常多的,jdk为我们做了较好的封装。

StringBuilder、StringBuffer

因为String是不可变的,所以提供了StringBuilder和StringBuffer这两种可变得字符串操作类。两者都实现了AbstractStringBuilder,其内部依然是利用char[]来实现的,不过此char数组是可变的。StringBuilder 与 StringBuffer 支持的所有操作基本上是一致的, 不同的是, StringBuilder 不需要执行同步。同步操作意味着要耗费系统的一些额外的开销, 或时间, 或空间, 或资源等, 甚至可能会造成死锁。从理论上来讲, StringBuilder 的速度要更快一些。下边以StingBuilder方法为例:
其构造方法为:

    //默认char[]容量为16
    public StringBuilder() {
        super(16);
    }
    //指定容量大小
    public StringBuilder(int capacity) {
        super(capacity);
    }
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }

append方法:

    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        //扩容  满足的最小容量count + len
        ensureCapacityInternal(count + len);
        //完成添加
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

    /**
     * For positive values of {@code minimumCapacity}, this method
     * behaves like {@code ensureCapacity}, however it is never
     * synchronized.
     * If {@code minimumCapacity} is non positive due to numeric
     * overflow, this method throws {@code OutOfMemoryError}.
     */
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            //返回新容量的char[]
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }
    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2;
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }

//返回新的char[]
    public static char[] copyOf(char[] original, int newLength) {
        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

     * @param      srcBegin   index of the first character in the string
     *                        to copy.
     * @param      srcEnd     index after the last character in the string
     *                        to copy.
     * @param      dst        the destination array.
     * @param      dstBegin   the start offset in the destination array.
     * @exception IndexOutOfBoundsException If any of the following
     *            is true:
     *            
  • {@code srcBegin} is negative. *
  • {@code srcBegin} is greater than {@code srcEnd} *
  • {@code srcEnd} is greater than the length of this * string *
  • {@code dstBegin} is negative *
  • {@code dstBegin+(srcEnd-srcBegin)} is larger than * {@code dst.length}
*/ 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); } System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); }

扩容的方法最终是由newCapacity()实现的,在这个方法中首先把容量扩大为原来的容量加2,如果此时仍小于指定的容量,那么就把新的容量设为minimumCapacity(原来的长度+添加的字符串长度)。然后判断是否溢出,如果溢出了,把容量设为Integer.MAX_VALUE。最后把value值进行拷贝,这显然是耗时操作。然后采用的字符拷贝操作。
append()是最常用的方法,它有很多形式的重载。上面是其中一种,用于追加字符串。如果str是null,则会调用appendNull()方法。这个方法其实是追加了’n’、’u’、’l’、’l’这几个字符。如果不是null,则首先扩容,然后调用String的getChars()方法将str追加到value末尾。最后返回对象本身,所以append()可以连续调用。
复制是最后调用的System的native方法实现的:

     * @param      src      the source array.
     * @param      srcPos   starting position in the source array.
     * @param      dest     the destination array.
     * @param      destPos  starting position in the destination data.
     * @param      length   the number of array elements to be copied.
     * @exception  IndexOutOfBoundsException  if copying would cause
     *               access of data outside array bounds.
     * @exception  ArrayStoreException  if an element in the src
     *               array could not be stored into the dest array
     *               because of a type mismatch.
     * @exception  NullPointerException if either src or
     *               dest is null.
     */
    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

字符串翻转:

    public AbstractStringBuilder reverse() {
        boolean hasSurrogates = false;
        int n = count - 1;
        for (int j = (n-1) >> 1; j >= 0; j--) {
            int k = n - j;
            char cj = value[j];
            char ck = value[k];
            value[j] = ck;
            value[k] = cj;
            if (Character.isSurrogate(cj) ||
                Character.isSurrogate(ck)) {
                hasSurrogates = true;
            }
        }
        if (hasSurrogates) {
            reverseAllValidSurrogatePairs();
        }
        return this;
    }

toString()方法返回一个字符串:

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

你可能感兴趣的:(源码解析)