Java-API简析_java.lang.String类(基于 Latest JDK)(浅析源码)

本文详细解析了Java中String类的构造方法和主要方法,包括字符串的创建、编码解码、字符操作、比较、拼接、搜索等功能,同时涵盖了Unicode编码和字符点的处理。通过对源码的分析,揭示了String类在处理字符串时的内部逻辑。
摘要由CSDN通过智能技术生成

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://blog.csdn.net/m0_69908381/article/details/130210804
出自【进步*于辰的博客

注:依赖类:CharacterArraysSystemLocalePattern

文章目录

1、概述

继承关系:

  • java.lang.Object
    • java.lang.String

所有已实现的接口:
Serializable、CharSequence、Comparable<String>


public final class String extends Object implements Serializable, Comparable<String>, CharSequence

String 类代表字符串。Java 程序中的所有 字符串字面值 \color{green}{字符串字面值} 字符串字面值(如"abc" )都作为此类的实例来实现。

字符串是常量;它们的值在创建之后不能改变。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享它们。例如:

String str = "abc";

等效于:

char data[] = {'a', 'b', 'c'};
String str = new String(data);

下面给出了一些如何使用字符串的更多例子:

sout "abc";
String cde = "cde";
sout "abc" + cde;
String c = "abc".substring(2,3);
String d = cde.substring(1, 2);

String 类包括的方法有:检查序列的单个字符;比较字符串;搜索字符串;提取子字符串;创建字符串副本,在该副本中,所有的字符都被转换为大写或小写形式。大小写映射基于 Character 类指定的 Unicode Standard 版本。

Java 语言提供对 字符串串联符号 \color{blue}{字符串串联符号} 字符串串联符号(“+”)和其他对象到字符串的转换的特殊支持。字符串串联是通过 StringBuilder(或 StringBuffer)类及其 append()实现的。字符串转换是通过 toString()实现的,该方法由 Object 类定义,并可被 Java 中所有类继承。有关字符串串联和转换的更多信息,请参阅 Gosling、Joy 和 Steele 合著的 《The Java Language Specification》。

注意: \color{red}{注意:} 注意:除非另行说明,否则将 null 参数传递给此类中的构造方法或方法都会抛出 NullPointerException

String 表示一个UTF-16格式的字符串,其中的增补字符 代理项对 \color{brown}{代理项对} 代理项对表示(有关详细信息,请参阅字符类中的 Unicode 字符表示形式)。索引值是指 char 代码单元,因此增补字符在 String 中占用两个位置

注: \color{red}{注:} 注:String 类提供处理 Unicode 代码点和 Unicode 代码单元的方法。 (关于代码点与代码单元的说明,见字符类)

从以下版本开始:
JDK1.0
另请参见:
Object.toString()、StringBuffer、StringBuilder、Charset、序列化表格

2、构造方法摘要

属性说明:

private final char value[];
private int hash; // 默认值: 0

2.1 null

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

public String() {
    this.value = "".value;
}

2.2 byte[] bytes

构造一个新的 String,方法是使用平台的默认字符集解码字节的指定数组。

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

调用第4项,故 offset 为0,length 为bytes.length

2.3 byte[] ascii, int hibyte

已过时。 该方法无法将字节正确转换为字符。从 JDK 1.1 起,完成该转换的首选方法是通过 String 构造方法,该方法接受一个字符集名称或使用平台的默认字符集。

2.4 byte[] bytes, int offset, int length

构造一个新的 String,方法是使用指定的字符集解码字节的指定子数组。即取 bytes 中从offset开始、length长度内的byte 序列构建字符串。

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

关于checkBounds()。见第5.3项。

后续补充。

2.5 byte[] ascii, int hibyte, int offset, int count

已过时。 该方法无法将字节正确转换为字符。从 JDK 1.1 开始,完成该转换的首选方法是通过 String 构造方法,它接受一个字符集名称,或者使用平台默认的字符集。

2.6 byte[] bytes, int offset, int length, String charsetName

构造一个新的 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);
}

关于checkBounds(),见第5.3项。

后续补充。

2.7 byte[] bytes, String charsetName

构造一个新的 String,方法是使用指定的字符集解码指定的字节数组。

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

调用上1项。

2.8 char[] value

分配一个新的 String,它表示当前字符数组参数中包含的字符序列。

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

关于copyOf(),见Arrays类的第2.16项。

底层逻辑:将字符序列value所有字符取出,赋值于当前字符序列。

2.9 char[] value, int offset, int count

分配一个新的 String,它包含来自该字符数组参数的一个子数组的字符。
在这里插入图片描述
关于copyOfRange(),见Arrays类的第2.17项。

2.10 int[] codePoints, int offset, int count

分配一个新的 String,它包含该 Unicode 代码点数组参数的一个子数组的字符。即先将int[]转为字符序列,再构造新 String。

2.11 String original

初始化一个新创建的 String 对象,表示一个与该参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的一个副本。

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

2.12 StringBuffer buffer

分配一个新的字符串,它包含当前包含在字符串缓冲区参数中的字符序列。

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

见下1项。

2.13 StringBuilder builder

分配一个新的字符串,它包含当前包含在字符串生成器参数中的字符序列。

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

可见,此项与上1项的底层都调用copyOf(),此方法见Arrays类的第2.16项。

底层逻辑:将字符串缓冲区/生成器中的所有字符取出,赋值于当前字符序列。

注意: \color{red}{注意:} 注意:上1项使用了同步锁synchronized,故线程安全,而此项不是。

3、方法摘要

3.1 char charAt(int index)

返回指定索引处的 char 值。

public char charAt(int index) {
	// value是此字符串的字符序列。类型是char[],末尾元素索引为value.length - 1
    if ((index < 0) || (index >= value.length)) {
        throw new StringIndexOutOfBoundsException(index);
    }
    return value[index];
}

3.2 int codePointAt(int index)

返回指定索引处的字符(Unicode 代码点),

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

关于codePointAtImpl(),见字符类的第6.1项。

3.3 int codePointBefore(int index)

返回指定索引之前的字符(Unicode 代码点),

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

关于codePointBeforeImpl(),见字符类的第6.2项。

3.4 int codePointCount(int beginIndex, int endIndex)

返回此 String 的指定文本范围中的 Unicode 代码点数。

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

关于codePointCountImpl(),见字符类的第6.3项。

3.5 int compareTo(String anotherString)

按字典顺序比较两个字符串。
在这里插入图片描述
示例:

"csdn".compareTo("csXn");// 12-----------A
"csdn".compareTo("cs");// 2--------------b

代码说明:

  1. 代码A。"csdn""csXn"长度相同,因此以"csdn"的字符序列作为遍历限制。它们的第一个不相同字符的索引是2,即"d""X"。其中,"d" A S C L L 码 \color{green}{ASCLL码} ASCLL是100,而"X"是88,故结果是12;
  2. 代码B。"csdn""cs"长度不相同,因此以"cs"的字符序列作为遍历限制。在"cs"的字符序列内,2个字符串的字符序列相同,故直接返回2字符串的长度差,得2。

3.6 int compareToIgnoreCase(String str)

不考虑大小写,按字典顺序比较两个字符串,

public int compareToIgnoreCase(String str) {
    return CASE_INSENSITIVE_ORDER.compare(this, str);
}

后续解析。

3.7 String concat(String str)

将指定字符串联到此字符串的结尾。

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

关于copyOf(),见 Arrays 类的第2.16项。
关于getChars(),见第5.4项。将str的字符序列追加到当前字符序列末尾,末尾后一位的索引自然是value.length,即图中的len

3.8 boolean contains(CharSequence s)

当且仅当此字符串包含 char 值的指定序列时,才返回 true。

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

关于indexOf(),见第25项。

3.9 boolean contentEquals(CharSequence cs)

当且仅当此 String 表示与指定序列相同的 char 值时,才返回 true。

/**
 * 1、CharSequence 接口的实现类有:此类、StringBuilder、StringBuffer、CharBuffer。
 * 2、此类底层逻辑类似 equals()。
 */
public boolean contentEquals(CharSequence cs) {
	// 若 cs 是 StringBuffer, StringBuilder
    if (cs instanceof AbstractStringBuilder) {
        if (cs instanceof StringBuffer) {
            synchronized(cs) {
               return nonSyncContentEquals((AbstractStringBuilder)cs);// 此方法同 equals()
            }
        } else {
            return nonSyncContentEquals((AbstractStringBuilder)cs);
        }
    }
    
    // 若 cs 是 String
    if (cs instanceof String) {
        return equals(cs);
    }
    
    // 若 cs 是“通用的” 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;
}

关于nonSyncContentEquals(),见第5.6项。

3.10 boolean contentEquals(StringBuffer sb)

当且仅当此 String 表示与指定的 StringBuffer 相同的字符序列时,才返回 true。

public boolean contentEquals(StringBuffer sb) {
    return contentEquals((CharSequence)sb);
}

调用上1项。

3.11 static String copyValueOf(char[] data)

返回指定数组中表示该字符序列的字符串。

public static String copyValueOf(char data[]) {
    return new String(data);
}

调用第2.8项。

3.12 static String copyValueOf(char[] data, int offset, int count)

返回指定数组中表示该字符序列的字符串。

public static String copyValueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}

调用第2.9项。

3.13 boolean endsWith(String suffix)

测试此字符串是否以指定的后缀结束。

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

调用第44项,第44项阐明,测试此字符串是否以指定前缀开始,对应toffset(开始索引)为0,即第43项。而若toffsetvalue.length - suffix.value.length,自然是:测试此字符串是否以指定后缀结束。

3.14 boolean equals(Object anObject)

比较此字符串与指定的对象。

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) {// 若2个字符串长度不相等,自然不相等,直接返回 false
            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;
}

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

后续解析。

3.16 static String format(Locale l, String format, Object… args)

使用指定的语言环境(语种)、格式字符串和参数返回一个格式化字符串。

public static String format(Locale l, String format, Object... args) {
    return new Formatter(l).format(format, args).toString();
}

示例:

String s1 = "지금은 %d 년, %s 년, %f 포인트 상승";// 韩语
String.format(Locale.KOREAN, s1, 2024, "平", 1.5);// 지금은 2024 년, 平 년, 1.500000 포인트 상승
String.format(Locale.CHINA, s1, 2024, "平", 1.5);// 지금은 2024 년, 平 년, 1.500000 포인트 상승

暂不知语种l是做什么的。

3.17 static String format(String format, Object… args)

使用指定的格式字符串和参数返回一个格式化字符串。

public static String format(String format, Object... args) {
    return new Formatter().format(format, args).toString();
}

后续解析。

示例:

String s1 = "现在是%d年,属%s年,上升了%f个百分点";
String.format(s1, 2024, "平", 1.5);// 现在是2024年,属平年,上升了1.500000个百分点

3.18 byte[] getBytes()

使用平台默认的字符集将此 String 解码为字节序列,并将结果存储到一个新的字节数组中。

public byte[] getBytes() {
    return StringCoding.encode(value, 0, value.length);
}

static byte[] encode(char[] ca, int off, int len) {
    String csn = Charset.defaultCharset().name();
    try {
        // use charset name encode() variant which provides caching.
        return encode(csn, ca, off, len);
    } catch (UnsupportedEncodingException x) {
        warnUnsupportedCharset(csn);
    }
    try {
        return encode("ISO-8859-1", ca, off, len);// 使用默认编码
    } catch (UnsupportedEncodingException x) {
        // If this code is hit during VM initialization, MessageUtils is
        // the only way we will be able to get any kind of error message.
        MessageUtils.err("ISO-8859-1 charset not available: "
                         + x.toString());
        // If we can not find ISO-8859-1 (a required encoding) then things
        // are seriously wrong with the installation.
        System.exit(1);
        return null;
    }
}

后续补充解析。

3.19 void getBytes(int srcBegin, int srcEnd, byte[] dst, int dstBegin)

已过时。 该方法无法将字符正确转换为字节。从 JDK 1.1 起,完成该转换的首选方法是通过getBytes()构造方法,该方法使用平台的默认字符集。

3.20 byte[] getBytes(String charsetName)

使用指定的字符集将此 String 解码为字节序列,并将结果存储到一个新的字节数组中。

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

在这里插入图片描述
后续补充解析。

示例:

String str = "博客之星";
byte[] bs1 = str.getBytes("ISO-8859-1");
byte[] bs2 = str.getBytes("UTF-8");
new String(bs1);// ????
new String(bs2);// 博客之星

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

将字符从此字符串复制到目标字符数组。
在这里插入图片描述
关于arraycopy(),见 System 类的第2.1项。

arraycopy()的第5个形参是length,表示复制几个字符。此方法提供了开始索引srcBegin和结束索引srcEnd。因此,length = srcEnd - srcBegin

底层逻辑:从当前字符序列中,以srcBegin开始,取出srcEnd - srcBegin个字符,从dstBegin开始存放入dst

示例:

String str = "###csdn###";
// 从当前字符串的字符序列中提取提取新字符序列,自然新字符序列 arr 的长度最好与 str 相同
char[] arr = new char[str.length()];
sout Arrays.toString(arr);// 打印:[ ,  ,  ,  ,  ,  ,  ,  ,  ,  ]

str.getChars(3, 7, arr1, 3);
Arrays.toString(arr);// [ ,  ,  , c, s, d, n,  ,  ,  ]

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

由此类的构造方法可见,字符串构造时未为hash赋初值,即赋初值在调用此方法时。

由上述源码可见,字符串的hash只生成一次,故两个内容相同的字符串的hash相同。

示例:

String s1 = "csdn";// 3063034
String s2 = "csdn";
s1.hashCode() == s2.hashCode();// true

3.23 int indexOf(int ch)

返回指定字符在此字符串中第一次出现处的索引。

public int indexOf(int ch) {
    return indexOf(ch, 0);
}

调用下1项,fromIndex指定为0,即从左往右检索。

3.24 int indexOf(int ch, int fromIndex)

从指定的索引开始搜索,返回在此字符串中第一次出现指定字符处的索引。
在这里插入图片描述
示例。

String str = "acbc";
str.indexOf(99, 2);// 3

99对应字符'c',故indexOf(99, 2)是搜索字符 ‘c’ 第2次出现处的索引。并且,只要指定fromIndex 大于等于2即可。

3.25 int indexOf(String str)

返回第一次出现的指定子字符串在此字符串中的索引。

public int indexOf(String str) {
    return indexOf(str, 0);
}

调用下1项,fromIndex指定为0,同样从左往右检索。

3.26 int indexOf(String str, int fromIndex)

从指定的索引处开始,返回第一次出现的指定子字符串在此字符串中的索引。(同第24项)

public int indexOf(String str, int fromIndex) {
    return indexOf(value, 0, value.length,
            str.value, 0, str.value.length, fromIndex);
}

调用第5.1项。

3.27 native String intern()

返回字符串对象的规范化表示形式。

可能大家不理解什么是“规范化表示形式”。意思是:

将调用它的对象尝试放入常量池,如果常量池中已存在,则返回指向常量池中的引用,如果没有就放入常量池,并且返回指向常量池中的引用。

示例:

String str1 = "Hello";
String str2 = str1 + "World";
String str3 = "HelloWorld";
str2 == str3;// false

str2 = str2.intern();
str2 == str3;// true

相应底层分析在博文《[Java]基本数据类型与引用类型赋值的底层分析》中的【String】一栏有具体说明。

3.28 int lastIndexOf(int ch)

返回最后一次出现的指定字符在此字符串中的索引。

public int lastIndexOf(int ch) {
    return lastIndexOf(ch, value.length - 1);
}

调用下1项。从下1项可知,是从右往左检索,故fromIndex指定为value.length - 1

3.29 int lastIndexOf(int ch, int fromIndex)

从指定的索引处开始进行后向搜索,返回最后一次出现的指定字符在此字符串中的索引。

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

关于lastIndexOfSupplementary(),见第5.5项。

后续补充解析。

3.30 int lastIndexOf(String str)

返回在此字符串中最右边出现的指定子字符串的索引。

public int lastIndexOf(String str) {
    return lastIndexOf(str, value.length);
}

调用下1项,fromIndex指定为 0,同样从左往右检索。

3.31 int lastIndexOf(String str, int fromIndex)

从指定的索引处开始向后搜索,返回在此字符串中最后一次出现的指定子字符串的索引。

public intlastIndexOf(String str, int fromIndex) {
    return lastIndexOf(value, 0, value.length,
            str.value, 0, str.value.length, fromIndex);
}

调用第5.2项。

3.32 int length()

返回此字符串的长度。

3.33 boolean matches(String regex)

判断此字符串是否匹配给定的正则表达式。

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

调用Pattern类的第3.5项。

示例:

String s = "x_x_x@163.com";
s.matches("\\w+@[\\w&&[^_]]+\\.com");// 模式:邮箱
s = "19211111111";
s.matches("192\\d{8}");// 模式:中国广电手机号
s = "431123199911111111";
s.matches("^[1-9]\\d{5}(18|19|([23]\\d))\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$");// 模式:十八位身份证号

如果大家不了解“模式”,可查阅博文《正则表达式全解析+常用示例》(转发);若要深入了解,可查阅Pattern类中的【正则表达式的构造摘要】。

示例中定义的正则表达式参考于这两篇文章:(转发)

  1. 手机号验证最新正则表达式
  2. 身份证正则表达式

3.34 int offsetByCodePoints(int index, int codePointOffset)

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

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

后续解析。

3.35 boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)

测试两个字符串区域是否相等。

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

此方法与下1项功能相同,而源码差异很大。

后续补充解析和示例。

3.36 boolean regionMatches(int toffset, String other, int ooffset, int len)

测试两个字符串区域是否相等。
在这里插入图片描述
解析图中红框部分: \color{red}{解析图中红框部分:} 解析图中红框部分:(以一个较为容易理解的“极端”情况为例)
示例:

String origin = "abc123", other = "123";
"abc123".regionMatches(3, "123", 0, 3);// true

这是两个字符串的字符序列图:
在这里插入图片描述

限制原因:

  1. toffset < 0ooffset < 0:因为下面循环比较时是直接将toffsetooffset作为下标。若小于 0,则直接抛出 ArrayIndexOutOfBoundsException;
  2. toffset > (long)value.length - len:其中value.length是origin的字符序列长度,是6。假设len = 3,则toffset > 3,取4试试。origin从索引为4处开始遍历,只有2, 3;而len为3,other从索引为0处开始遍历,有1, 2, 3。两者不相同,返回false,即限制了origin的遍历范围
  3. ooffset > (long)other.value.length - len:其中other.value.length是other的字符序列长度,为3。假设len = 3,则ooffset > 0。取1试试,显然other仅遍历了2, 3,而origin遍历了1, 2, 3,两者不相同,返回false,即限制了other的遍历范围

这4个条件只要满足其中一个,两个字符串的字符序列的遍历范围就不可能相同,自然都返回false。

注意: \color{red}{注意:} 注意:源码中遍历比较部分挺巧妙的。len-- > 0控制循环,先比较,后自减;ta[to++]获取字符序列字符,先获取,再自增。这可比for循环常规写法优化多了。

P S : \color{brown}{PS:} PS当我写完再回头看,自己都云里雾里,解析源码的算法就是这样。只能说, 只可意会,不可言传 \color{green}{只可意会,不可言传} 只可意会,不可言传。不过这段阐述对大家应该有参考价值,大家若想理解这其中的算法,还得自行推演了。

3.37 String replace(char oldChar, char newChar)

返回由 newChar 替换此字符串中出现的所有 oldChar 而生成的一个新的字符串。
在这里插入图片描述
概述中说道,字符串是常量,故本身不可变。
因此,底层逻辑是:

new char[] → 拷贝value替换后的所有字符 → new String()的顺序重新构造字符串。

3.38 String replace(CharSequence target, CharSequence replacement)

使用指定的字面值替换序列替换此字符串匹配字面值目标序列的每个子字符串。

3.39 String replaceAll(String regex, String replacement)

使用给定的 replacement 字符串替换此字符串匹配给定的正则表达式的每个子字符串。

3.40 String replaceFirst(String regex, String replacement)

使用给定的 replacement 字符串替换此字符串匹配给定的正则表达式的第一个子字符串。

3.41 String[] split(String regex)

根据给定的正则表达式的匹配来拆分此字符串。

public String[] split(String regex) {
    return split(regex, 0);
}

调用下1项。

3.42 String[] split(String regex, int limit)

根据匹配给定的正则表达式来拆分此字符串。

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

代码说明:

  1. regex.value.length == 1 && ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1表示:regex仅1个字符,且不属于字符串".$|()[{^?*+\\"中的任一字符;
  2. regex.length() == 2 && regex.charAt(0) == '\\' && (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 && ((ch-'a')|('z'-ch)) < 0 && ((ch-'A')|('Z'-ch)) < 0表示:regex包含2个字符,第1个字符是\\,第2个字符不是0 ~ 9A ~ Za ~ z中的任一字符。
    注:关于类如((ch-'a')|('z'-ch)) < 0即可判断变量ch不是a ~ z中任一字符,我暂且不明白其实现原理,但经测试的确如此;

后续补充解析和示例。

3.43 boolean startsWith(String prefix)

测试此字符串是否以指定的前缀开始。

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

调用下1项,toffset为 0,表示从头开始比较。

3.44 boolean startsWith(String prefix, int toffset)

测试此字符串是否以指定前缀开始,该前缀以指定索引开始。
在这里插入图片描述

3.46 String substring(int beginIndex)

从指定的开始索引开始“截取”当前字符串。

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

由第2.9项构造字符串。

3.47 String substring(int beginIndex, int endIndex)

从指定的开始索引开始到指定的结束索引,“截取”当前字符串。

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

由第2.9项构造字符串。

3.48 char[] 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;
}

关于arraycopy(),见 System 类的第2.1项。

底层逻辑:构造新char[],再调用arraycopy()进行元素赋值。

3.49 String toLowerCase()

使用默认语言环境的规则将此 String 中的所有字符都转换为小写。

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

调用下1项。

所谓默认语言环境的规则,即Locale.getDefault(),见 Locale 类的第4.5项。

3.50 String toLowerCase(Locale locale)

使用给定 Locale 的规则将此 String 中的所有字符都转换为小写。

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

后续解析。

3.51 String toString()

返回此对象本身(它已经是一个字符串!)。

3.52 String toUpperCase()

使用默认语言环境的规则将此 String 中的所有字符都转换为大写。

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

调用下1项。

3.53 String toUpperCase(Locale locale)

使用给定的 Locale 规则将此 String 中的所有字符都转换为大写。

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

后续解析和示例。

3.54 boolean isEmpty()

判断当前字符序列是否为空。

3.55 String trim()

返回字符串的副本,忽略前导空白和尾部空白。
在这里插入图片描述
st初始值为0len初始值为value.length,故st < len可用于 循环限制 循环限制 循环限制
返回this的条件,从上下文判断,是:(st == 0) && (len == value.length),即当前字符串前后均无' '(空格)。
从这2个循环可知,此方法只能去除此字符串前后的空格,而不能去除中间的。

关于substring(),见第47项。

3.56 static String valueOf(boolean b)

返回 boolean 参数的字符串表示形式。

public static String valueOf(boolean b) {
    return b ? "true" : "false";
}

3.57 static String valueOf(char c)

返回 char 参数的字符串表示形式。

public static String valueOf(char c) {
    char data[] = {c};
    return new String(data, true);
}

调用第4.1项。

3 58 static String valueOf(char[] data)

返回 char 数组参数的字符串表示形式。

public static String valueOf(char data[]) {
    return new String(data);
}

调用第2.8项。

3.59 static String valueOf(char[] data, int offset, int count)

返回 char 数组参数的特定子数组的字符串表示形式。

public static String valueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}

调用第2.9项。

3.60 static String valueOf(double d)

返回 double 参数的字符串表示形式。

public static String valueOf(double d) {
    return Double.toString(d);
}

关于 Double 包装类,后续补充。

3.61 static String valueOf(float f)

返回 float 参数的字符串表示形式。

public static String valueOf(float f) {
    return Float.toString(f);
}

调用Float类的第4.20项。

3.62 static String valueOf(int i)

返回 int 参数的字符串表示形式。

public static String valueOf(int i) {
    return Integer.toString(i);
}

调用Integer类的第4.31项。

3.63 static String valueOf(long l)

返回 long 参数的字符串表示形式。

public static String valueOf(long l) {
    return Long.toString(l);
}

关于 Long 包装类,后续补充。

3.64 static String valueOf(Object obj)

返回 Object 参数的字符串表示形式。

public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

4、构造方法摘要(不开放)

4.1 char[] value, boolean share

以指定字符数组作为字符序列构造字符串。注意:指定字符数组与此字符串对象的字符序列共享。

String(char[] value, boolean share) {
    this.value = value;
}

可能大家不理解“共享”是什么意思,我举个例大家就明白了。

示例1。

char[] buf = {'C', 'S', 'd', 'N'};
String s = new String(buf);
Arrays.toString(buf);// [C, S, d, N]
sout s;// CSdN

buf[2] = 'D';
Arrays.toString(buf);// [C, S, D, N]
sout s;// CSdN

示例2。

char[] buf = {'C', 'S', 'd', 'N'};

Constructor const = z1.getDeclaredConstructor(char[].class, boolean.class);
const.setAccessible(true);
String s = (String) const.newInstance(buf, true);
Arrays.toString(buf);// [C, S, d, N]
sout s;// CSdN

buf[2] = 'D';
Arrays.toString(buf);// [C, S, D, N]
sout s;// CSDN

share是一个标记,表示此构造方法的机制属于“共享”。目前,在底层中并没有使用此参数进行判断是否“共享”。

5、方法摘要(不开放)

5.1 static int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex)

从指定索引开始(向右检索),在部分源字符数组中,查找部分目标字符数组第一次出现的位置。

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

此方法的源码与第2.24项差异较大,但逻辑相似,后续解析。

示例:

Class z1 = String.class;
String sourceStr = "csdn/开源中国/csdn";
String targetStr = "#csdn";

// 构建源字符数组
Method getCharsMethod = z1.getDeclaredMethod("getChars", char[].class, int.class);// 见第5.4项
getCharsMethod.setAccessible(true);
char[] source = new char[sourceStr.length() + 2];// +2 的目的是让首尾各留一个空格,便于最后检查结果
getCharsMethod.invoke(sourceStr, source, 1);
// 结果:source = [ , c, s, d, n, /, 开, 源, 中, 国, /, c, s, d, n,  ]

// 构建目标字符数组
char[] target = new char[targetStr.length() + 2];
targetStr.getChars(1, targetStr.length(), target, 1);// 见第2.21项
// 结果:target = [ , c, s, d, n,  ,  ]

Method indexOfMethod = z1.getDeclaredMethod("indexOf", char[].class, int.class, int.class, char[].class, int.class, int.class, int.class);
indexOfMethod.setAccessible(true);
int r1 = (int)indexOfMethod.invoke(null, source, 0, source.length, target, 1, 4, 0);// 结果:1
int r2 = (int)indexOfMethod.invoke(null, source, 0, source.length, target, 1, 4, 5);// 结果:11

5.2 static int lastIndexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex)

从指定索引开始(向左检索),在部分源字符数组中,查找部分目标字符数组最后一次出现的位置。

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

此方法的源码与第2.29项差异较大,但逻辑相似,后续解析。

示例:

Class z1 = String.class;
String sourceStr = "csdn/开源中国/csdn";
String targetStr = "#csdn";

// 构建源字符数组
Method getCharsMethod = z1.getDeclaredMethod("getChars", char[].class, int.class);// 见第5.4项
getCharsMethod.setAccessible(true);
char[] source = new char[sourceStr.length() + 2];// +2 的目的是让首尾各留一个空格,便于最后检查结果
getCharsMethod.invoke(sourceStr, source, 1);
// 结果:source = [ , c, s, d, n, /, 开, 源, 中, 国, /, c, s, d, n,  ]

// 构建目标字符数组
char[] target = new char[targetStr.length() + 2];
targetStr.getChars(1, targetStr.length(), target, 1);// 见第2.21项
// 结果:target = [ , c, s, d, n,  ,  ]

Method lastIndexOfMethod = z1.getDeclaredMethod("lastIndexOf", char[].class, int.class, int.class, char[].class, int.class, int.class, int.class);
lastIndexOfMethod.setAccessible(true);
int r1 = (int)lastIndexOfMethod.invoke(null, source, 0, source.length, target, 1, 4, source.length - 1);// 结果:11
int r2 = (int)lastIndexOfMethod.invoke(null, source, 0, source.length, target, 1, 4, source.length - 6);// 结果:1

5.3 private static void checkBounds(byte[] bytes, int offset, int length)

检查指定开始索引和长度是否超出指定字符序列范围。

private staticvoid checkBounds(byte[] bytes, int offset, int length) {
    if (length < 0)
        throw new StringIndexOutOfBoundsException(length);
    if (offset < 0)
        throw new StringIndexOutOfBoundsException(offset);
    if (offset > bytes.length - length)
        throw new StringIndexOutOfBoundsException(offset + length);
}

5.4 void getChars(char dst[], int dstBegin)

将当前字符序列中所有字符覆盖于指定字符数组,从指定索引开始。

void getChars(char dst[], int dstBegin) {
    System.arraycopy(value, 0, dst, dstBegin, value.length);
}

此方法与第2.21项类似,只是仅提供了目标字符序列dst[]和目标字符序列开始索引dstBegin
因此,arraycopy()的第2个实参“当前字符序列开始索引”是0;第5个实参“提取长度”是value.length(当前字符序列长度,即复制所有)。

注意: \color{red}{注意:} 注意:此项与第2.21项都提供了目标字符序列开始索引dstBegin,故都具有 元素覆盖 元素覆盖 元素覆盖功能。(注:第2.7项,是元素追加,属于元素覆盖的一种)

示例:

String str = "csdn";
char[] arr = new char[str.length() + 2];

Class strClass = str.getClass();
Method getCharsMethod = strClass .getDeclaredMethod("getChars", char[].class, int.class);
getCharsMethod .setAccessible(true);
getCharsMethod .invoke(str, arr, 1);
sout Arrays.toString(arr);// 打印:[ , c, s, d, n,  ]

5.5 private int lastIndexOfSupplementary(int ch, int fromIndex)

(业务米明。)

private int lastIndexOfSupplementary(int ch, int fromIndex) {
    if (Character.isValidCodePoint(ch)) {
        final char[] value = this.value;
        char hi = Character.highSurrogate(ch);
        char lo = Character.lowSurrogate(ch);
        int i = Math.min(fromIndex, value.length - 2);
        for (; i >= 0; i--) {
            if (value[i] == hi && value[i + 1] == lo) {
                return i;
            }
        }
    }
    return -1;
}

关于isValidCodePoint/highSurrogate()/lowSurrogate(),见字符类的第5.60、5.78、5.79项。

5.6 private boolean nonSyncContentEquals(AbstractStringBuilder sb)

判断此字符串与指定对象是否相等,功能上同equals()

// AbstractStringBuilder 抽象类的两个子类分别是 StringBuilder、StringBuffer。
private boolean nonSyncContentEquals(AbstractStringBuilder sb) {
    char v1[] = value;
    char v2[] = sb.getValue();
    int n = v1.length;
    if (n != sb.length()) {
        return false;
    }
    for (int i = 0; i < n; i++) {
        if (v1[i] != v2[i]) {
            return false;
        }
    }
    return true;
}

6、字段摘要

6.1 static Comparator<String> CASE_INSENSITIVE_ORDER

一个排序 String 对象的 Comparator,它的作用与 compareToIgnoreCase() 相同。

最后

如果大家需要Java-API文档,我上传了《Java-API文档-包含5/8/11三个版本》。


本文暂缓更新。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进步·于辰

感谢打赏!很高兴可以帮到你!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值