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

来源:互联网 发布:品质退款率被淘宝管控 编辑:程序博客网 时间:2024/06/01 07:40

首先向搞懂常量池的知识点:触摸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是采用下图中的方式实现的:
这里写图片描述
这实际上式jvm对+操作符的重载优化,但是这也有效率问题,当有多个连接符+时,会创建多个StringBuilder。所以对于可变的字符串,一般要采用StringBuilder和StringBuffer来实现。

String方法
string实现的方法有:implements java.io.Serializable, Comparable, CharSequence ,表明它是可序列化,以及重写了compareTo方法。
首先来看它的构造方法:
这里写图片描述
可以看到,我们可以利用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还提供了很多替换和匹配算法:
这里写图片描述
它是利用正则来实现的,比如:

    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:     *            <ul><li>{@code srcBegin} is negative.     *            <li>{@code srcBegin} is greater than {@code srcEnd}     *            <li>{@code srcEnd} is greater than the length of this     *                string     *            <li>{@code dstBegin} is negative     *            <li>{@code dstBegin+(srcEnd-srcBegin)} is larger than     *                {@code dst.length}</ul>     */    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 <code>src</code>     *               array could not be stored into the <code>dest</code> array     *               because of a type mismatch.     * @exception  NullPointerException if either <code>src</code> or     *               <code>dest</code> is <code>null</code>.     */    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);    }
阅读全文
0 0