JDK源码学习--String类

String类

String对象不可变,所以可以共享,当对象创建完之后,该对象的内容(字符序列)是不允许改变的,细看源码发现内容改变则会new一个新的String对象,返回到原地址中。String类维护的char数组被final所修饰,String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,而原来的字符串还是存在的,并且产生垃圾


一、实现接口

	public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence

String类实现了Serializable接口支持序列化和反序列化,实现了Comparable接口支持字符串的比较,实现CharSequence接口说明它是一个字符序列。


二、String成员变量

    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

String底层使用一个char字符数组来维护并且是final类型,不能被改变,操作字符串实际上就是操作这个字符数组,所以只要一个值改变就会生成一个新的String类型对象。


三、构造方法

1、无参构造器

初始化一个新创建的 String 对象,使其表示一个空字符序列。

	public String() {
        this.value = new char[0];
    }

2、String 参数

有参构造方法,常用的通过构造方法创建对象的方法。

	public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

延伸:创建字符串对象两种方式

  • 直接赋值方式创建,对象存储在方法区的常量池
String str1="ABCD";//直接赋值的方式
  • 通过本构造方法创建,字符串对象存储在堆内存
String str2=new String("ABCD");//实例化的方式

那么这两种方式有什么不同呢?
面试题1:上述的两种方法分别创建了几个String对象?

首先你需要知道JVM的内存模型:常量池(专门存储常量的地方,在方法区中)分为编译常量池(存储字节码的相关信息)和运行常量池(存储常量数据)。
当执行方式1的时候,会在常量池中添加一个新的ABCD字符,str指向常量池的ABCD
当执行方式2的时候,通过new操作符,在堆空间新开辟一块空间用来存储新的String对象。而此时常量池中已经有了ABCD字符,堆中的String对象直接指向常量池中的ABCD,而str则指向堆空间中的String对象。
在这里插入图片描述
方式1:如果常量池中,存在”ABCD”,那么str1直接引用,此时不创建String对象.否则,先在常量池先创建”ABCD”内存空间,再引用.
方式2:首先通过new关键字会在堆空间创建一块新的内存区域,至少会创建一个String对象。最多创建两个String对象(当常量池里面没有“ABCD”时就会先在常量池里面创建,然后在内存空间里面创建一个)。

面试题2:两种实例化方式字符串的比较

public class TestString {
    public static void main(String[] args) {
        String str1 = "Lance";
        String str2 = new String("Lance");
        String str3 = str2; 
        String str4 = "Lance";
       
        System.out.println(str1==str2);//false
        System.out.println(str1.equals(str2));//true
        System.out.println(str1==str3);//false
        System.out.println(str3==str2);//true
        System.out.println(str1==str4);//true
    }
}

使用==,只能比较引用的内存地址是否相同,使用equals方法,则比较的是字符串的内容。
当执行方式1的时候,会在常量池中添加一个新的Lance字符,str指向常量池的Lance
当执行方式2的时候,通过new在堆空间分配一块空间存储新的String对象。此时常量池中已经有了Lance字符,堆中的String对象直接指向常量池中的Lance,而str2则指向堆空间中的String对象。
str1==str2比较的是内存地址,因此返回false
str1.equals(str2)比较的是字符串内容,因此返回true
str1==str3引用传递,str3直接指向st2的堆内存地址,比较的是内存地址,因此返回false
str3==str2str3直接指向st2的堆内存地址,内存地址相同,返回true
str1==str4内存地址相同,返回true

==equals的区别
==在对字符串比较的时候,对比的是内存地址,而equals比较的是字符串内容,在开发的过程中,equals()通过接受参数,可以避免空指向。

String str = null;
if(str.equals("hello")){//此时会出现空指向异常
 ...
}
if("hello".equals(str)){//此时equals会处理null值,可以避免空指向异常
 ...
}

3、char[]参数

通过char数组参数来构造String对象,实际将入参char数组值复制给String对象的成员变量value[]。

public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);
}

通过入参数组的一部分来构造String对象,offset为数组被使用的开始位置,count元素的个数,传入非法参数会报数组越界异常StringIndexOutOfBoundsException

 	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);
    }

4、int[]参数

通过int数组构造String对象,第一步计算char的精确大小,第二步分配并填充char[],Character.isBmpCodePoint(int codePoint)确定指定的字符(Unicode代码点)是否在Basic Multilingual Plane(BMP)中。Character.isValidCodePoint©确定指定的代码点是否是一个有效的Unicode代码点值,如果指定的代码点值在MIN_CODE_POINT和MAX_CODE_POINT(含)之间此方法返回true,否则返回false。

	public String(int[] codePoints, 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 > 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;
    }

5、byte[]参数

通过使用指定的字符集解码指定的 byte 子数组,构造一个新的 String,checkBounds(bytes, offset, length)是对参数进行检查,该方法是私有的只能在String类中调用。

public String(byte bytes[], int offset, int length, String charsetName)
        throws UnsupportedEncodingException {
    if (charsetName == null)
        throw new NullPointerException("charsetName");
    checkBounds(bytes, offset, length);
    this.value = StringCoding.decode(charsetName, bytes, offset, length);
}

通过使用指定的 charset 解码指定的 byte 子数组,构造一个新的 String。Charset与charsetName是java中表示字符集的两种不同形式。它们之间相互转换如下:
字符串转Charset对象:Charset charset = Charset.forName(“UTF-8”);
Charset对象转字符串:String s = charset.displayName();

	public String(byte bytes[], int offset, int length, Charset charset) {
        if (charset == null)
            throw new NullPointerException("charset");
        checkBounds(bytes, offset, length);
        this.value =  StringCoding.decode(charset, bytes, offset, length);
    }

通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String,内部调用上述方法。

public String(byte bytes[], String charsetName)
        throws UnsupportedEncodingException {
    this(bytes, 0, bytes.length, charsetName);
}

通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String,内部调用上述方法。

public String(byte bytes[], Charset charset) {
    this(bytes, 0, bytes.length, charset);
}

通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String

public String(byte bytes[], int offset, int length) {
    checkBounds(bytes, offset, length);
    this.value = StringCoding.decode(bytes, offset, length);
}

通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。

public String(byte bytes[]) {
    this(bytes, 0, bytes.length);
}

6、StringBuilder,StringBuffer参数

通过StringBuffer分配一个新的字符串,它包含字符串缓冲区参数中当前包含的字符序列。StringBuffer内部维护了一个char[],这里将StringBuffer数组中的内容复制给String对象中的数组,并且加了synchronized块保证线程安全。

	public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }

通过StringBuilder分配一个新的字符串,它包含字符串生成器参数中当前包含的字符序列,同样是将StringBuilder数组中的内容复制给String对象中的数组。

public String(StringBuilder builder) {
    this.value = Arrays.copyOf(builder.getValue(), builder.length());
}

7、常用方法

length():

获取字符串长度,返回字符串中所包含的字符数目,即char数组的长度

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

isEmpty():

判断字符串是否为空,即判断value数组的长度为0即可

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

charAt(int index):

按下标获取单个字符,返回指定索引处的 char 值。

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

codePointAt(int index):

获取字符串指定索引位置的字符的代码点。

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

codePointBefore(int index):

获取字符串指定索引位置前一个字符的代码点。

	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);
    }

codePointCount(int beginIndex, int endIndex):

获取字符串beginIndex-endIndex区间内代码点个数

	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);
    }

getChars(int srcBegin, int srcEnd, char dst[], int dstBegin)

获取子串,把它拷贝到目标字符数组

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
    if (srcBegin < 0) { // 如果源数组开始位置<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);
}

getBytes(String charsetName)

使用给定的 charset 将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。

	public byte[] getBytes(String charsetName)throws UnsupportedEncodingException {
        if (charsetName == null) throw new NullPointerException();
        return StringCoding.encode(charsetName, value, 0, value.length);
    }
    public byte[] getBytes(Charset charset) {
        if (charset == null) throw new NullPointerException();
        return StringCoding.encode(charset, value, 0, value.length);
    }
    public byte[] getBytes() {
        return StringCoding.encode(value, 0, value.length);
    }

equals(Object anObject)

equals方法重写了Object类的equals方法,会判断两个字符串的每一个字符是否相等。比较两个引用指向的String对象内容是否相同,将此字符串与指定的对象比较。当且仅当该参数不为 null,并且是与此对象表示相同字符序列的 String 对象时,结果才为 true。

public boolean equals(Object anObject) {
    if (this == anObject) { // 如果两个引用指向的是同一个String对象
        return true;
    }
    if (anObject instanceof String) { // 如果第2个引用指向的对象是String实例
        String anotherString = (String)anObject; // 强制类型转换
        int n = value.length; // 获取第1个引用指向的String对象的字符串长度
        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;
}

equalsIgnoreCase(String anotherString)

将此 String 与另一个 String 比较,不考虑大小写。如果两个字符串的长度相同,并且其中的相应字符都相等(忽略大小写),则认为这两个字符串是相等的。

	public boolean equalsIgnoreCase(String anotherString) {
        return (this == anotherString) ? true
                : (anotherString != null)
                && (anotherString.value.length == value.length)
                && regionMatches(true, 0, anotherString, 0, value.length);
    }
    
    判断两个字符串在忽略大小写的情况下是否相等,主要调用regionMatches方法
    public boolean regionMatches(boolean ignoreCase, 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) {
            char c1 = ta[to++];
            char c2 = pa[po++];
            //在这里先行判断,如果相等就直接跳过后面即可,可以提高效率
            if (c1 == c2) {
                continue;
            }
            if (ignoreCase) {
                // If characters don't match but case may be ignored,
                // try converting both characters to uppercase.
                // If the results match, then the comparison scan should
                // continue.
                char u1 = Character.toUpperCase(c1);
                char u2 = Character.toUpperCase(c2);
                //都转换成大写的形式,如果相等,则跳过
                if (u1 == u2) {
                    continue;
                }
                // Unfortunately, conversion to uppercase does not work properly
                // for the Georgian alphabet, which has strange rules about case
                // conversion.  So we need to make one last check before
                // exiting.
                if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
                    continue;
                }
            }
            return false;
        }
        return true;
    }

compareTo(String anotherString)

compareTo实现Comparable接口的方法用于比较两个String对象的大小,如果两个字符串的字符序列相等,则返回0;不相等时,从两个字符串第0个字符开始比较,返回第一个不相等的字符差。另一种情况,较长的字符串的前面部分恰好是较短的字符串,则返回他们的长度差。

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;
}

startsWith(String prefix, int toffset) & startsWith(String prefix)

测试此字符串从指定索引开始的子字符串是否以指定前缀开始

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;
}
 
//测试此字符串是否以指定的前缀开始。 
public boolean startsWith(String prefix) {
    return startsWith(prefix, 0);
}

endsWith(String suffix)

调用了startsWith方法,测试此字符串是否以指定的后缀结束。

public boolean endsWith(String suffix) {
    return startsWith(suffix, value.length - suffix.value.length);
}

hashCode()

重写Object的hashCode方法获取散列码方法,返回此字符串的哈希码。String 对象的哈希码根据以下公式计算: s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1] 使用 int 算法,这里 s[i] 是字符串的第 i 个字符,n 是字符串的长度,^ 表示求幂。(空字符串的哈希值为 0。)

public int hashCode() {
    // 获取字符串缓存散列码
    int h = hash;
    if (h == 0 && value.length > 0) { // 如果字符串缓存散列码为0并且字符串数组长度大于0
        // 定义字符数组指针
        char val[] = value;
 
        // 遍历每个字符
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i]; // 31 * h会被JVM优化成(h << 5) - h
        }
        hash = h; // 修改字符串缓存散列码
    }
    return h;
}

indexOf 方法集

//获取指定字符在字符串中第一次出现的索引位置
public int indexOf(int ch) {
    return indexOf(ch, 0); // 从0开始查找
}

// 获取在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。Unicode指统一码
public int indexOf(int ch, int fromIndex) {
    // 获取字符数组长度
    final int max = value.length;
    if (fromIndex < 0) { // 如果起始位置<0
        fromIndex = 0; // 起始位置置0
    } else if (fromIndex >= max) { // 如果起始位置>=字符数组长度
        return -1;
    }

    // Character.MIN_SUPPLEMENTARY_CODE_POINT是BmpCode代码点,如果ch是非辅助代码点或者负值(无效代码点)
    if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { 
        final char[] value = this.value; // 创建字符数组的指针
        for (int i = fromIndex; i < max; i++) { // 从fromIndex开始遍历每个字符
            if (value[i] == ch) { // 如果找到ch字符
                return i;
            }
        }
        return -1;
    } else { // 寻找ch在辅助部分中的索引
        return indexOfSupplementary(ch, fromIndex);
    }
}
 
//返回指定字符在此字符串中最后一次出现处的索引。
public int lastIndexOf(int ch) {
    return lastIndexOf(ch, value.length - 1);
}
 
//返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索。
public int lastIndexOf(int ch, int fromIndex) {
    if (ch < Character.MIN_SUPPLEMENTARY_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);
    }
}
 
//返回指定子字符串在此字符串中第一次出现处的索引。
public int indexOf(String str) {
    return indexOf(str, 0);
}
 
//返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。
public int indexOf(String str, int fromIndex) {
    return indexOf(value, 0, value.length,
            str.value, 0, str.value.length, fromIndex);
}
 
 
// 获取参数子串在该字符串中从起始位置开始第一次出现的位置
static int indexOf(char[] source, int sourceOffset, int sourceCount,
        String target, int fromIndex) {
    return indexOf(source, sourceOffset, sourceCount,
                   target.value, 0, target.value.length,
                   fromIndex);
}
 
    /* @param   source       the characters being searched.//这里就是value数组
     * @param   sourceOffset offset of the source string./ //源字符串的偏移量
     * @param   sourceCount  count of the source string.    //这里是value数组的长度
     * @param   target       the characters being searched for.  //待搜索目标字符串
     * @param   targetOffset offset of the target string.   //待搜索目标字符串的偏移量
     * @param   targetCount  count of the target string.   //待搜索目标字符串的长度
     * @param   fromIndex    the index to begin searching from. //起始位置
     */
    // 获取参数子串在该字符串中从起始位置开始第一次出现的位置
    // source是目标串(该字符串),target是模式串(子串)
    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);//搜索第一个匹配的字符时所能达到的最大值,因为要保证后面的长度>=targetCount
 
        // 朴素匹配算法
        //下面这里就是核心搜索算法了,会先匹配第一个字符,然后依次向后移,直到完全匹配
        //或者是匹配到max仍然没有匹配成功
        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 */
            //可以注意这里i下标只是用来匹配第一个字符,因为有可能部分匹配时,需要从先在匹配
            //所以这里重新应用下标j
            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;
    }//当匹配失败时,返回-1
 
//返回指定子字符串在此字符串中最右边出现处的索引。
public int lastIndexOf(String str) {
    return lastIndexOf(str, value.length);
}
 
public int lastIndexOf(String str, int fromIndex) {
    return lastIndexOf(value, 0, value.length,
            str.value, 0, str.value.length, fromIndex);
}

substring方法集

//返回一个新的字符串,它是此字符串的一个子字符串。该子字符串从指定索引处的字符开始,直到此字符串末尾。
public String substring(int beginIndex) {
    if (beginIndex < 0) { // 如果起始下标<0
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    int subLen = value.length - beginIndex; // 获取截取长度
    if (subLen < 0) { // 如果截取长度<0
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
 
//返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,直到索引 endIndex - 1 处的字符。因此,该子字符串的长度为 endIndex-beginIndex。 
public String substring(int beginIndex, int endIndex) {
    if (beginIndex < 0) { // 如果起始下标<0
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    if (endIndex > value.length) { // 如果末尾下标>字符数组长度
        throw new StringIndexOutOfBoundsException(endIndex);
    }
    int subLen = endIndex - beginIndex; // 获取截取长度
    if (subLen < 0) { // 如果截取长度<0
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return ((beginIndex == 0) && (endIndex == value.length)) ? this
            : new String(value, beginIndex, subLen);
}

concat(String str)

将指定字符串连接到此字符串的结尾,如果参数字符串的长度为 0,则返回此 String 对象。否则,创建一个新的 String 对象,用来表示由此 String 对象表示的字符序列和参数字符串表示的字符序列连接而成的字符序列。

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

replace(char oldChar, char newChar)

将字符串中所有的旧字符oldChar,替换为新的字符newChar。逻辑是:先找到字符串中第一次出现oldChar字符的位置i。将之前的字符数组复制给新数组buf,然后从i后将字符数组中的内容复制给buf,只不过如果字符为oldChar则替换为newChar,然后再通过buf创建新的字符串返回。

	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;
    }

matches(String regex)

校验此字符串是否匹配给定的正则表达式,匹配返回true,不匹配返回false。

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

contains(CharSequence s)

判断给定字符串中是否包含指定的字符序列。实际是调用indexOf方法,查找字符序列在字符串中的位置来判断

	public boolean contains(CharSequence s) {
        return indexOf(s.toString()) > -1;
    }

split方法集

		//根据指定规则切割字符串,切割全部子串。内部调用split(String regex, int limit)方法,limit为0
		public String[] split(String regex) { 
        	return split(regex, 0);
        }
		//根据给定正则表达式的匹配拆分此字符串。
        public String[] split(String regex, int limit) {
        char ch = 0;
        if (((regex.value.length == 1 && //判断参数长度是否为1
             ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) || //判断参数不在特殊符号".$|()[{^?*+\\"中 
             (regex.length() == 2 && //判断参数长度是否为2
              regex.charAt(0) == '\\' &&  //第一位为转义符"\\"
              (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 && //第二位不是0-9之间 '0'转换为int为48 '9'转换为int为57 
              ((ch-'a')|('z'-ch)) < 0 && //判断不在 a-z之间
              ((ch-'A')|('Z'-ch)) < 0)) && //判断不在A-Z之间
            (ch < Character.MIN_HIGH_SURROGATE || 
             ch > Character.MAX_LOW_SURROGATE))  //判断分隔符不在特殊符号中
        {
            int off = 0;//当前索引
            int next = 0;//下一个分割符出现的索引
            boolean limited = limit > 0;//只分割前limit份还是全部分割,limit=0代表全部分割
            ArrayList<String> list = new ArrayList<>();//创建一个集合,用于存放切割好的子串
            while ((next = indexOf(ch, off)) != -1) {//判断是否包含下个分隔符,如果有则进入循环
                if (!limited || list.size() < limit - 1) {//判断是全部分割或当前分割次数小于总分割次数
                    list.add(substring(off, next));//切割当前索引到下一个分隔符之间的字符串并添加到list中
                    off = next + 1; //继续切割下一下子串
                } else {    // last one
                    //assert (list.size() == limit - 1);
                    list.add(substring(off, value.length));//切割当前索引到字符串结尾的子字符串并添加到list
                    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).length() == 0) {
                    resultSize--;
                }
            }
            String[] result = new String[resultSize];//创建对应长度数组,因为返回结果为字符串数组
            return list.subList(0, resultSize).toArray(result);//集合转数组并返回
        }
        return Pattern.compile(regex).split(this, limit);//其它情况用正则的切割规则去切割
    }

方法集

join方法作用是将字符序列数组或集合通过分割符delimiter连接成一个字符串。第一个方法使用的是可变参数,第二个方法使用的可迭代参数,通过遍历数组或集合将数组元素或集合元素添加到StringBuilder,添加前会先加入一个分割符delimiter,然后将StringBuilder中的内容返回,

	public static String join(CharSequence delimiter, CharSequence... elements) {
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
        // Number of elements not likely worth Arrays.stream overhead.
        StringJoiner joiner = new StringJoiner(delimiter);
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        return joiner.toString();
    }
	public static String join(CharSequence delimiter,
            Iterable<? extends CharSequence> elements) {
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
        StringJoiner joiner = new StringJoiner(delimiter);
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        return joiner.toString();
    }
    public StringJoiner add(CharSequence newElement) {
        prepareBuilder().append(newElement);
        return this;
    }
    private StringBuilder prepareBuilder() {
        if (value != null) {
            value.append(delimiter);
        } else {
            value = new StringBuilder().append(prefix);
        }
        return value;
    }

toLowerCase()、toUpperCase()

英文大小写转换方法

	public String toLowerCase() {
        return toLowerCase(Locale.getDefault());
    }
    public String toUpperCase() {
        return toUpperCase(Locale.getDefault());
    }

trim()

这个方法是去掉首尾的空格,而实现方式也非常简单,分别找到第一个非空格字符的下标,与最后一个非空格字符的下标,然后返回之间的子字符串。注意这里由于应用了substring方法,所以len变量的控制要小心

	public String trim() {
        int len = value.length;
        int st = 0;
        char[] val = value;   
        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        while ((st < len) && (val[len - 1] <= ' ')) {
            len--;
        }
        return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
    }

toString()

返回字符串对象的字符串形式

	public String toString() {
        return this;
    }

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;
    }

format方法集

	//使用指定的格式字符串和参数返回一个格式化字符串
	public static String format(String format, Object... args) {
        return new Formatter().format(format, args).toString();
    }
	//使用指定的语言环境、格式字符串和参数返回一个格式化字符串。
    public static String format(Locale l, String format, Object... args) {
        return new Formatter(l).format(format, args).toString();
    }

valueOf方法集

//返回 Object 参数的字符串表示形式。
public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}
 
//返回 char 数组参数的字符串表示形式。字符数组的内容已被复制,后续修改不会影响新创建的字符串。 
public static String valueOf(char data[]) {
    return new String(data);
}
 
//返回 char 数组参数的特定子数组的字符串表示形式。 
//offset 参数是子数组的第一个字符的索引。count 参数指定子数组的长度。字符数组的内容已被复制,后续修改不会影响新创建的字符串。 
public static String valueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}
 
//返回指定数组中表示该字符序列的 String。 
public static String copyValueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}
 
//返回指定数组中表示该字符序列的 String。 
public static String copyValueOf(char data[]) {
    return new String(data);
}
 
//返回 boolean 参数的字符串表示形式。 
public static String valueOf(boolean b) {
    return b ? "true" : "false";
}
 
//返回 char 参数的字符串表示形式。 
public static String valueOf(char c) {
    char data[] = {c};
    return new String(data, true);
}
 
//返回 int 参数的字符串表示形式。 
public static String valueOf(int i) {
    return Integer.toString(i);
}
 
//返回 long 参数的字符串表示形式。 
public static String valueOf(long l) {
    return Long.toString(l);
}
 
//返回 float 参数的字符串表示形式。 
public static String valueOf(float f) {
    return Float.toString(f);
}
 
//返回 double 参数的字符串表示形式。 
public static String valueOf(double d) {
    return Double.toString(d);
}
 
//本地方法,把该字符串存入常量池,返回此字符串的引用
public native String intern();</code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code>

public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}
 
//返回 char 数组参数的字符串表示形式。字符数组的内容已被复制,后续修改不会影响新创建的字符串。 
public static String valueOf(char data[]) {
    return new String(data);
}
 
//返回 char 数组参数的特定子数组的字符串表示形式。 
//offset 参数是子数组的第一个字符的索引。count 参数指定子数组的长度。字符数组的内容已被复制,后续修改不会影响新创建的字符串。 
public static String valueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}
 
//返回指定数组中表示该字符序列的 String。 
public static String copyValueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}
 
//返回指定数组中表示该字符序列的 String。 
public static String copyValueOf(char data[]) {
    return new String(data);
}
 
//返回 boolean 参数的字符串表示形式。 
public static String valueOf(boolean b) {
    return b ? "true" : "false";
}
 
//返回 char 参数的字符串表示形式。 
public static String valueOf(char c) {
    char data[] = {c};
    return new String(data, true);
}
 
//返回 int 参数的字符串表示形式。 
public static String valueOf(int i) {
    return Integer.toString(i);
}
 
//返回 long 参数的字符串表示形式。 
public static String valueOf(long l) {
    return Long.toString(l);
}
 
//返回 float 参数的字符串表示形式。 
public static String valueOf(float f) {
    return Float.toString(f);
}
 
//返回 double 参数的字符串表示形式。 
public static String valueOf(double d) {
    return Double.toString(d);
}

上一篇:JDK源码学习-Object类

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章