文章目录

  • String 部分源码阅读
  • 类声明
  • String 字段解释
  • String 构造方法阅读
  • public String(char value[], int offset, int count)
  • public String(int[] codePoints, int offset, int count)
  • 查询
  • 关于char
  • charAt()
  • startsWith()、endsWith()
  • 关于codePoint
  • codePointAt()
  • codePointBefore()
  • codePointCount()
  • offsetByCodePoints()
  • contains()
  • 关于index
  • indexOf()
  • lastIndexOf()
  • 比较
  • 关于compare
  • compareTo()
  • compareToIgnoreCase()
  • 关于equals
  • equals()
  • equalsIgnoreCase()
  • contentEquals()
  • 关于matches
  • matches()
  • regionMatches()
  • 替换
  • repalce()
  • replaceFirst()
  • replaceAll()
  • 截取
  • subSequence()
  • subString()
  • 赋值
  • copyValueOf()
  • getChars()
  • 连接
  • concat()
  • 分割
  • split()
  • 转换
  • format()
  • intern()
  • getBytes()
  • toCharArray()
  • toUpperCase()
  • toLowerCase()
  • toString()
  • valueOf()
  • 其他
  • trim()
  • hashCode()
  • isEmpty()
  • length()


String 部分源码阅读

类声明

public final class String implements java.io.Serializable, Comparable, CharSequence
String类是一个公有的、不可更改的的类,实现了三个接口:
Serializable(实现后String是可序列化的)
Comparable(实现CompareTo(String s)方法,用于String的比较)
CharSequence(其中
lenth()返回字符串长度,
charAt(int index)可以获取index处的字符,
subSequence(int start ,int end),顾名思义,sub表示“子的,次的”,再结合给出的参数arguement start 和end,可以知道这个是求取子字符串,
toString()方法是将对象转化成需要的字符串再返回,对于String对象,则是返回String自己

String 字段解释

String源码详解

String 构造方法阅读

String有很多构造方法,有参无参和不同类型的参数,这里就选取自己跳跳能够得着的几个方法进行分析(后来不小心把所有源码都复制了上来,但是还有很多没有分析理解的)

public String(char value[], int offset, int count)
  1. 这一段是对此方法的总注解,仔细读读可以知道方法的大体功能,将一段子数组赋给创建的新String,同时要注意开端位置offset和将要被复制字符个数count的合法性。
Allocates a new {@code String} that contains characters from a subarray
of the character array argument. The {@code offset} argument is the
index of the first character of the subarray and the {@code count}
argument specifies the length of the subarray. The contents of the
subarray are copied; subsequent modification of the character array does
not affect the newly created string.
  1. 下面是对方法具体参数的注解,
@param  value
        Array that is the source of characters
@param  offset
        The initial offset
@param  count
        The length
@throws  IndexOutOfBoundsException
         If the {@code offset} and {@code count} arguments index
         characters outside the bounds of the {@code value} array

其中,
@param
command @param param denotes following word is name of the parameter and following text is description of the parameter; all parameters are placed in a list
表示 接下来是参数的名字和对它的描述
@code
To mark some text as a code in documentation, code and endcode commands are used.
为了将某些文本标记为代码,使用code 和endcode

  1. 再看具体实现代码
public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {
                this.value = "".value;
                return;
            }//如果count为0且offset合法,则新创建的数组长度为0,但没有存任何东西
        }
        // 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);
    }

这里的Note注释和大小比较时奇怪的摆放位置肯定让大家困惑,可以参考链接里的一个解释
Note: offset or count might be near -1>>>1.

public String(int[] codePoints, int offset, int count)
  1. 大体注释
Allocates a new {@code String} that contains characters from a subarray
of the <a href="Character.html#unicode">Unicode code point</a> array
argument.  The {@code offset} argument is the index of the first code
point of the subarray and the {@code count} argument specifies the
length of the subarray.  The contents of the subarray are converted to
{@code char}s; subsequent modification of the {@code int} array does not
affect the newly created string.

和上一种实现方法的解释相比,增加了几个需要注意的点,1.来源是一个存放代码点的数组,2.规定了第一个要复制的字符和长度之后,将代码点转化成对应的字符,再放进新数组。

关于代码点的知识,这里附上链接,可以理解成是一种表示字符的编码,就像ascii码一样
代码点(Code Point)和代码单元(Code Unit)

  1. 具体实现代码
public String(int[] codePoints, int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= codePoints.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > codePoints.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        //至此,都和上一个方法一样

        final int end = offset + count;

        // Pass 1: Compute precise size of char[]
        int n = count;
        for (int i = offset; i < end; i++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                continue;
            else if (Character.isValidCodePoint(c))
                n++;
            else throw new IllegalArgumentException(Integer.toString(c));
        }

        // Pass 2: Allocate and fill in char[]
        final char[] v = new char[n];

        for (int i = offset, j = 0; i < end; i++, j++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                v[j] = (char)c;
            else
                Character.toSurrogates(c, v, j++);
        }

        this.value = v;
    }

本人水平还不够,没法讲清楚,供大家自己思考吧
之后下面的代码,容易理解的就直接放实现方法了

查询

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

对于下标index,必须要检查它的合法性,过大过小都会抛出异常

startsWith()、endsWith()
public boolean startsWith(String prefix, int toffset) {
        char ta[] = value;
        int to = toffset;
        char pa[] = prefix.value;
        int po = 0;
        int pc = prefix.value.length;
        // Note: toffset might be near -1>>>1.
        if ((toffset < 0) || (toffset > value.length - pc)) {
            return false;
        }
        while (--pc >= 0) {
            if (ta[to++] != pa[po++]) {
                return false;
            }
        }
        return true;
    }

判断从toffset开始的String 字串是否以prefix 开头,
先定义局部变量存储传入的prefix和toffset,
照例对索引进行范围判断,
然后是熟悉地遍历数组,判断每个字符就好

public boolean startsWith(String prefix) {
        return startsWith(prefix, 0);
    }

没有toffset传入就默认是0

关于codePoint
codePointAt()
public int codePointAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return Character.codePointAtImpl(value, index, value.length);
    }

返回指定位置的代码点(Unicode编码)
首先判断index的合法性
再而用Character的codePointAtImpl方法返回对应的Unicode值
对于此引用的方法,附上链接,讲得很清楚(总之就是要知道这个位置字符的编码,但是有个小问题:有些比较“肥”的字符占据了两个数组位置,所以它的编码是两个位置合起来的结果)
Java学习笔记6 - Unicode编码与UTF-16

codePointBefore()
public int codePointBefore(int index) {
        int i = index - 1;
        if ((i < 0) || (i >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return Character.codePointBeforeImpl(value, index, 0);
    }

这里则是返回index前一个位置的代码点(字符的编码),用到了Character.codePointBeforeImpl

codePointCount()
public int codePointCount(int beginIndex, int endIndex) {
        if (beginIndex < 0 || endIndex > value.length || beginIndex > endIndex) {
            throw new IndexOutOfBoundsException();
        }
        return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex);
    }

返回给定范围内的代码点数量,代码点数量就是字符个数,但有些字符比较“肥”,占了数组中两个位置,所以这个方法还是有必要的;
当开始下标为负或结束下标超出数组长度(结束下标一般不被包括,所以最大可以为数组长度)或开始下标大于结束下标时,异常。咦?那假如开始等于结束呢,不清楚,好像也不行欸,欢迎大家发表想法。

offsetByCodePoints()
public int offsetByCodePoints(int index, int codePointOffset) {
        if (index < 0 || index > value.length) {
            throw new IndexOutOfBoundsException();
        }
        return Character.offsetByCodePointsImpl(value, 0, value.length,
                index, codePointOffset);
    }

返回此String中从给定索引index处偏移codePointOffset个代码点的索引

String str = "hello";
int n = str.offsetByCodePoints(0,3);
System.out.println(n);//输出 3;
contains()
public boolean contains(CharSequence s) {
        return indexOf(s.toString()) > -1;
    }

哦哦,只要看看它的下标是不是大于-1就好啦,因为在indexOf的方法里,不符合的都返回-1了!

关于index
indexOf()
public int indexOf(int ch, int fromIndex) {
        final int max = value.length;
        if (fromIndex < 0) {
            fromIndex = 0;
        } else if (fromIndex >= max) {
            // Note: fromIndex might be near -1>>>1.
            return -1;
        }

        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
            // handle most cases here (ch is a BMP code point or a
            // negative value (invalid code point))
            final char[] value = this.value;
            for (int i = fromIndex; i < max; i++) {
                if (value[i] == ch) {
                    return i;
                }
            }
            return -1;
        } else {
            return indexOfSupplementary(ch, fromIndex);
        }
    }

从指定的offset处查找字符ch 在字符串中出现的第一个位置
若offset为负,结果会同offset为0一样,若offset大于等于字符串长,均会返回-1;
若ch是占两个字节,也就是一个数组位置的字符,那么就从offset位置遍历字符串进行比较,返回正确的下标,没有则返回-1;
若ch占4个字节,也就是两个数组位置,那么采用indexOfSupplementary方法,(找寻增补字符的下标的方法)也就是两个数组位置上的“子字符”都要相等才能说明这个字符相等。

当然,indexOf还有很多形参类型,比如将字符换成字符串等。

lastIndexOf()
public int lastIndexOf(int ch, int fromIndex) {
        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
            // handle most cases here (ch is a BMP code point or a
            // negative value (invalid code point))
            final char[] value = this.value;
            int i = Math.min(fromIndex, value.length - 1);
            for (; i >= 0; i--) {
                if (value[i] == ch) {
                    return i;
                }
            }
            return -1;
        } else {
            return lastIndexOfSupplementary(ch, fromIndex);
        }
    }

哈哈,其实就是倒着找第一个出现的就好啦,和上面的indexOf基本类似

比较

关于compare
compareTo()
public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;

        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }

比较两个String,但是会返回他们的字符差值或是长度差值,代码简单清晰,很是好看纳
先用len1和len2保存长度,lim存放短一点的长度,v1和v2是两个数组,将两个数组中字符一一比较,中途若有不等,返回字符差值,否则是直到一方数组已经结束,这时两个数组只有长度上的差异,返回长度差值

compareToIgnoreCase()

忽略大小写,进行比较

关于equals
equals()
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

判断一个对象的内容是否与此String相等,
若二者“==”成立,则一定相等, 等号是判断对象是否一样,equals是判断二者内容是否相同,等号要求更严;
anObject instanceof String,instanceof的左边是对象,右边是类或接口,当左边的对象是右边类或子类实现的对象时,返回true,否则返回false,在对引用数据类进行强制转换时会用到;
转换之后对二者的每个字符一一遍历,只有全部相等时才返回true,否则,只要有一个不等,或是instanceof返回为false,则整个方法返回false

equalsIgnoreCase()
public boolean equalsIgnoreCase(String anotherString) {
        return (this == anotherString) ? true
                : (anotherString != null)
                && (anotherString.value.length == value.length)
                && regionMatches(true, 0, anotherString, 0, value.length);
    }
contentEquals()
public boolean contentEquals(CharSequence cs) {
        // Argument is a StringBuffer, StringBuilder
        if (cs instanceof AbstractStringBuilder) {
            if (cs instanceof StringBuffer) {
                synchronized(cs) {
                   return nonSyncContentEquals((AbstractStringBuilder)cs);
                }
            } else {
                return nonSyncContentEquals((AbstractStringBuilder)cs);
            }
        }
        // Argument is a String
        if (cs instanceof String) {
            return equals(cs);
        }
        // Argument is a generic CharSequence
        char v1[] = value;
        int n = v1.length;
        if (n != cs.length()) {
            return false;
        }
        for (int i = 0; i < n; i++) {
            if (v1[i] != cs.charAt(i)) {
                return false;
            }
        }
        return true;
    }

暂时还没达到那个水平分析,先放放

关于matches
matches()
public boolean matches(String regex) {
        return Pattern.matches(regex, this);
    }

pan’d

regionMatches()
public boolean regionMatches(int toffset, String other, int ooffset,
            int len) {
        char ta[] = value;
        int to = toffset;
        char pa[] = other.value;
        int po = ooffset;
        // Note: toffset, ooffset, or len might be near -1>>>1.
        if ((ooffset < 0) || (toffset < 0)
                || (toffset > (long)value.length - len)
                || (ooffset > (long)other.value.length - len)) {
            return false;
        }
        while (len-- > 0) {
            if (ta[to++] != pa[po++]) {
                return false;
            }
        }
        return true;
    }

判断两个String在指定区域内是否内容相同,思路在方法前的解释都写得很清楚,参数是offset、other string、toffset和len,两个起始位置,另一个字符串,和它们的长度

先判断给定索引是否合法,不合法返回false
再对两个字符串的字符遍历比较,中途有不同的可以直接返回false,只有当索引合法且均相同时才返回true

还有另一种参数形式,更麻烦一些应该,这里就不分析啦

替换

repalce()
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;
                }
            }
            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++;
                }
                return new String(buf, true);
            }
        }
        return this;
    }

将一个字符串中的所有某种字符替换成另一种字符,是先将前面所有非需要替换字符赋给一个新数组,之后若遇到要替换的字符,换,否则,直接放进去;你可能会想,为什么不直接替换呢,非要新建,本来以为和增补字符有关,后来查了一查,源码的设计很精妙,在最后不需替换的字符串和长字符串面前,效率比直接替换快的多,突然又感受到数据结构的神奇代码魅力了,这里有相关回答:
为什么replace源码那么长

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

将第一次出现的regex正则表达式替换成replacement字符串

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

将所有regex字符串均替换

截取

subSequence()
public CharSequence subSequence(int beginIndex, int endIndex) {
        return this.substring(beginIndex, endIndex);
    }

哈哈,官方发话,就是下一个方法

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

返回从指定位置开始的子串,首先依旧是对offset合法性的判断,抛出不同异常,最后返回是原本的或是新建的一个字符串,思路还是比较简单的

赋值

copyValueOf()
getChars()
void getChars(char dst[], int dstBegin) {
        System.arraycopy(value, 0, dst, dstBegin, value.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);
    }

void getChars(char dst[], int dstBegin)
将String 中的字符传到dst数组中,从dstBegin开始传;
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)
将String中子串传过去,同样注意索引(下标)的合法性

连接

concat()
public String concat(String str) {
        if (str.isEmpty()) {
            return this;
        }
        int len = value.length;
        int otherLen = str.length();
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }

将一个字符串连接到另一个后面
是采用新建一个buf数组的方式,先将value传入,再将str传进去,再返回即可(我的暂时很浅显,没有细细去看)

分割

split()
public String[] split(String regex, int limit) {
        /* fastpath if the regex is a
         (1)one-char String and this character is not one of the
            RegEx's meta characters ".$|()[{^?*+\\", or
         (2)two-char String and the first char is the backslash and
            the second is not the ascii digit or ascii letter.
         */
        char ch = 0;
        if (((regex.value.length == 1 &&
             ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
             (regex.length() == 2 &&
              regex.charAt(0) == '\\' &&
              (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
              ((ch-'a')|('z'-ch)) < 0 &&
              ((ch-'A')|('Z'-ch)) < 0)) &&
            (ch < Character.MIN_HIGH_SURROGATE ||
             ch > Character.MAX_LOW_SURROGATE))
        {
            int off = 0;
            int next = 0;
            boolean limited = limit > 0;
            ArrayList<String> list = new ArrayList<>();
            while ((next = indexOf(ch, off)) != -1) {
                if (!limited || list.size() < limit - 1) {
                    list.add(substring(off, next));
                    off = next + 1;
                } else {    // last one
                    //assert (list.size() == limit - 1);
                    list.add(substring(off, value.length));
                    off = value.length;
                    break;
                }
            }
            // If no match was found, return this
            if (off == 0)
                return new String[]{this};

            // Add remaining segment
            if (!limited || list.size() < limit)
                list.add(substring(off, value.length));

            // Construct result
            int resultSize = list.size();
            if (limit == 0) {
                while (resultSize > 0 && list.get(resultSize - 1).isEmpty()) {
                    resultSize--;
                }
            }
            String[] result = new String[resultSize];
            return list.subList(0, resultSize).toArray(result);
        }
        return Pattern.compile(regex).split(this, limit);
    }

split好长,之后再来看吧……

转换

format()
public static String format(String format, Object... args) {
        return new Formatter().format(format, args).toString();
    }
intern()
public native String intern();
getBytes()
public void getBytes(int srcBegin, int srcEnd, byte 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);
        }
        Objects.requireNonNull(dst);

        int j = dstBegin;
        int n = srcEnd;
        int i = srcBegin;
        char[] val = value;   /* avoid getfield opcode */

        while (i < n) {
            dst[j++] = (byte)val[i++];
        }
    }

将给定子串转化 为dst字节数组的一部分(从dstBegin开始)
先对索引进行判断,
Objects.requireNonNull(dst);是判断对象 dst数组是否为空,若空,则报异常;
之后再一个一个地强制转换,赋给dst数组

还有很多其他参数的getBytes方法,这里就不看啦

toCharArray()
public char[] toCharArray() {
        // Cannot use Arrays.copyOf because of class initialization order issues
        char result[] = new char[value.length];
        System.arraycopy(value, 0, result, 0, value.length);
        return result;
    }
toUpperCase()
public String toUpperCase(Locale locale) {
        if (locale == null) {
            throw new NullPointerException();
        }

        int firstLower;
        final int len = value.length;

        /* Now check if there are any characters that need to be changed. */
        scan: {
            for (firstLower = 0 ; firstLower < len; ) {
                int c = (int)value[firstLower];
                int srcCount;
                if ((c >= Character.MIN_HIGH_SURROGATE)
                        && (c <= Character.MAX_HIGH_SURROGATE)) {
                    c = codePointAt(firstLower);
                    srcCount = Character.charCount(c);
                } else {
                    srcCount = 1;
                }
                int upperCaseChar = Character.toUpperCaseEx(c);
                if ((upperCaseChar == Character.ERROR)
                        || (c != upperCaseChar)) {
                    break scan;
                }
                firstLower += srcCount;
            }
            return this;
        }

        /* result may grow, so i+resultOffset is the write location in result */
        int resultOffset = 0;
        char[] result = new char[len]; /* may grow */

        /* Just copy the first few upperCase characters. */
        System.arraycopy(value, 0, result, 0, firstLower);

        String lang = locale.getLanguage();
        boolean localeDependent =
                (lang == "tr" || lang == "az" || lang == "lt");
        char[] upperCharArray;
        int upperChar;
        int srcChar;
        int srcCount;
        for (int i = firstLower; i < len; i += srcCount) {
            srcChar = (int)value[i];
            if ((char)srcChar >= Character.MIN_HIGH_SURROGATE &&
                (char)srcChar <= Character.MAX_HIGH_SURROGATE) {
                srcChar = codePointAt(i);
                srcCount = Character.charCount(srcChar);
            } else {
                srcCount = 1;
            }
            if (localeDependent) {
                upperChar = ConditionalSpecialCasing.toUpperCaseEx(this, i, locale);
            } else {
                upperChar = Character.toUpperCaseEx(srcChar);
            }
            if ((upperChar == Character.ERROR)
                    || (upperChar >= Character.MIN_SUPPLEMENTARY_CODE_POINT)) {
                if (upperChar == Character.ERROR) {
                    if (localeDependent) {
                        upperCharArray =
                                ConditionalSpecialCasing.toUpperCaseCharArray(this, i, locale);
                    } else {
                        upperCharArray = Character.toUpperCaseCharArray(srcChar);
                    }
                } else if (srcCount == 2) {
                    resultOffset += Character.toChars(upperChar, result, i + resultOffset) - srcCount;
                    continue;
                } else {
                    upperCharArray = Character.toChars(upperChar);
                }

                /* Grow result if needed */
                int mapLen = upperCharArray.length;
                if (mapLen > srcCount) {
                    char[] result2 = new char[result.length + mapLen - srcCount];
                    System.arraycopy(result, 0, result2, 0, i + resultOffset);
                    result = result2;
                }
                for (int x = 0; x < mapLen; ++x) {
                    result[i + resultOffset + x] = upperCharArray[x];
                }
                resultOffset += (mapLen - srcCount);
            } else {
                result[i + resultOffset] = (char)upperChar;
            }
        }
        return new String(result, 0, len + resultOffset);
    }
toLowerCase()
public String toLowerCase(Locale locale) {
        if (locale == null) {
            throw new NullPointerException();
        }

        int firstUpper;
        final int len = value.length;

        /* Now check if there are any characters that need to be changed. */
        scan: {
            for (firstUpper = 0 ; firstUpper < len; ) {
                char c = value[firstUpper];
                if ((c >= Character.MIN_HIGH_SURROGATE)
                        && (c <= Character.MAX_HIGH_SURROGATE)) {
                    int supplChar = codePointAt(firstUpper);
                    if (supplChar != Character.toLowerCase(supplChar)) {
                        break scan;
                    }
                    firstUpper += Character.charCount(supplChar);
                } else {
                    if (c != Character.toLowerCase(c)) {
                        break scan;
                    }
                    firstUpper++;
                }
            }
            return this;
        }

        char[] result = new char[len];
        int resultOffset = 0;  /* result may grow, so i+resultOffset
                                * is the write location in result */

        /* Just copy the first few lowerCase characters. */
        System.arraycopy(value, 0, result, 0, firstUpper);

        String lang = locale.getLanguage();
        boolean localeDependent =
                (lang == "tr" || lang == "az" || lang == "lt");
        char[] lowerCharArray;
        int lowerChar;
        int srcChar;
        int srcCount;
        for (int i = firstUpper; i < len; i += srcCount) {
            srcChar = (int)value[i];
            if ((char)srcChar >= Character.MIN_HIGH_SURROGATE
                    && (char)srcChar <= Character.MAX_HIGH_SURROGATE) {
                srcChar = codePointAt(i);
                srcCount = Character.charCount(srcChar);
            } else {
                srcCount = 1;
            }
            if (localeDependent ||
                srcChar == '\u03A3' || // GREEK CAPITAL LETTER SIGMA
                srcChar == '\u0130') { // LATIN CAPITAL LETTER I WITH DOT ABOVE
                lowerChar = ConditionalSpecialCasing.toLowerCaseEx(this, i, locale);
            } else {
                lowerChar = Character.toLowerCase(srcChar);
            }
            if ((lowerChar == Character.ERROR)
                    || (lowerChar >= Character.MIN_SUPPLEMENTARY_CODE_POINT)) {
                if (lowerChar == Character.ERROR) {
                    lowerCharArray =
                            ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale);
                } else if (srcCount == 2) {
                    resultOffset += Character.toChars(lowerChar, result, i + resultOffset) - srcCount;
                    continue;
                } else {
                    lowerCharArray = Character.toChars(lowerChar);
                }

                /* Grow result if needed */
                int mapLen = lowerCharArray.length;
                if (mapLen > srcCount) {
                    char[] result2 = new char[result.length + mapLen - srcCount];
                    System.arraycopy(result, 0, result2, 0, i + resultOffset);
                    result = result2;
                }
                for (int x = 0; x < mapLen; ++x) {
                    result[i + resultOffset + x] = lowerCharArray[x];
                }
                resultOffset += (mapLen - srcCount);
            } else {
                result[i + resultOffset] = (char)lowerChar;
            }
        }
        return new String(result, 0, len + resultOffset);
    }
toString()
public String toString() {
        return this;
    }
valueOf()
public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

其他

trim()
public String trim() {
        int len = value.length;
        int st = 0;
        char[] val = value;    /* avoid getfield opcode */

        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        while ((st < len) && (val[len - 1] <= ' ')) {
            len--;
        }
        return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
    }
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;
    }

emmmm,这里放上一个解释
Java编程:String 类中 hashCode() 方法详解

isEmpty()
public boolean isEmpty() {
        return value.length == 0;
    }

如果长度为0 即为空o

length()
public int length() {
        return value.length;
    }