首页 > 代码库 > String源码分析

String源码分析

String类内部维护了一个char[]类型的value用来存储字符串,相对来说源码较为简单些。

1.不可变性

String的不可变主要体现在三个方面:

  • String类被定义为final类型,不可被继承
  • String中的value[]被定义为final
  • String中的所有生成新的String的操作底层都调用Array.copy或者System.copy来生成一个新的String对象
2.构造函数,String的构造函数较为简单,但以下几个较为特殊
    public String(String original) {
        this.value = http://www.mamicode.com/original.value;"unshared not supported";
        this.value = http://www.mamicode.com/value;>

以上两个是较为特殊的一类构造函数,第一个用一个现成的String来初始化一个新的String对象,构造方法直接将新的String对象的value指向老的value对象。由于String是不可变的,所以这里不需要重新copy一份value的对象。第二个构造方法看似会破坏String类型的不可变性(当参数value变化时String也会变化),但该构造方法并没有声明为public,只允许在包内使用,被声明为public的String(char value[])底层调用的是Array.copy来实现底层数据拷贝的,上述的两个构造函数已经不建议使用

    public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = http://www.mamicode.com/Arrays.copyOfRange(value, offset, offset+count);>

上面这个构造函数较为典型,很多其他的构造函数都与其类似或者底层调用了该构造函数,入参是一个char数组(byte[]),offset偏移位置及count偏移量。底层调用Arrays.copy函数来进行深度复制。

    public String(StringBuffer buffer) {
        synchronized(buffer) {  //保证线程安全
            this.value = http://www.mamicode.com/Arrays.copyOf(buffer.getValue(), buffer.length());>

上述两个构造函数入参分别是StringBuffer和StringBuilder,底层都是调用Arrays.copyOf,唯一不同的是StringBuffer是线程安全的,所有调用时需要用synchronized关键字。

3.其他方法
    static int indexOf(char[] source, int sourceOffset, int sourceCount,
            char[] target, int targetOffset, int targetCount,
            int fromIndex) {
        if (fromIndex >= sourceCount) {
            return (targetCount == 0 ? sourceCount : -1);
        }
        if (fromIndex < 0) {
            fromIndex = 0;
        }
        if (targetCount == 0) {
            return fromIndex;
        }

        char first = target[targetOffset];
        int max = sourceOffset + (sourceCount - targetCount);

        for (int i = sourceOffset + fromIndex; i <= max; i++) {
            /* Look for first character. */
            if (source[i] != first) {
                while (++i <= max && source[i] != first);
            }

            /* Found first character, now look at the rest of v2 */
            if (i <= max) {
                int j = i + 1;
                int end = j + targetCount - 1;
                for (int k = targetOffset + 1; j < end && source[j]
                        == target[k]; j++, k++);

                if (j == end) {
                    /* Found whole string. */
                    return i - sourceOffset;
                }
            }
        }
        return -1;
    }
    
    static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,
            char[] target, int targetOffset, int targetCount,
            int fromIndex) {
        /*
         * Check arguments; return immediately where possible. For
         * consistency, don‘t check for null str.
         */
        int rightIndex = sourceCount - targetCount;
        if (fromIndex < 0) {
            return -1;
        }
        if (fromIndex > rightIndex) {
            fromIndex = rightIndex;
        }
        /* Empty string always matches. */
        if (targetCount == 0) {
            return fromIndex;
        }

        int strLastIndex = targetOffset + targetCount - 1;
        char strLastChar = target[strLastIndex];
        int min = sourceOffset + targetCount - 1;
        int i = min + fromIndex;

        startSearchForLastChar:
        while (true) {
            while (i >= min && source[i] != strLastChar) {
                i--;
            }
            if (i < min) {
                return -1;
            }
            int j = i - 1;
            int start = j - (targetCount - 1);
            int k = strLastIndex - 1;

            while (j > start) {
                if (source[j--] != target[k--]) {
                    i--;
                    continue startSearchForLastChar;
                }
            }
            return start - sourceOffset + 1;
        }
    }

indexOf和lastIndexOf主要是index和lastIndex函数的底层调用,通读代码会发现底层实现并没有特别牛逼的kmp算法,仍然是一个字符一个字符扫描实现的。其中lastIndexOf还是用了continue startSearchForLastChar;相对来说比较少见。

    public String replace(char oldChar, char newChar) {
        if (oldChar != newChar) {
            int len = value.length;
            int i = -1;
            char[] val = value; /* avoid getfield opcode */

            while (++i < len) {
                if (val[i] == oldChar) {
                    break;
                }
            }
            //如果找不到则返回this
            if (i < len) {
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
                    buf[j] = val[j];
                }
                while (i < len) {
                    char c = val[i]; //替换
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                //返回新的String,利用上述包内的非public构造函数
                return new String(buf, true);
            }
        }
        return this;
    }

replace用于将String对象中的一个字符替换为另一个字符,如果找不到制定的字符则返回本身,如果找到则会另建一个新的String对象返回。

---恢复内容结束---

String源码分析