欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  Java

String源码的实例解读

程序员文章站 2022-05-13 12:53:34
...
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 = original.value;
        this.hash = original.hash;
    }
    
    String(char[] value, boolean share) {
        // assert share : "unshared not supported";
        this.value = 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 = Arrays.copyOfRange(value, offset, offset+count);
    }

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

    public String(StringBuffer buffer) {
        synchronized(buffer) {  //保证线程安全
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }
    
    public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.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源码的实例解读的详细内容,更多请关注其它相关文章!