【后端之旅】源码分析 String 篇

98 阅读12分钟

String 是我们开发 Java 项目需要面对次数最多的一个类。它有哪些功能,它能干什么不能干什么,我们都应当了如指掌。

关键字:String, Java, Java String, Java 源码

静态常量

  • String.CASE_INSENSITIVE_ORDER - 比较两个字符串时忽略大小写的比较器

    使用方式是调用 String.CASE_INSENSITIVE_ORDER.compare(String str1, String str2) 方法来比较两个字符串。

    该比较器并没有考虑本地化内容的比较规则。

静态方法

  • String.indexOf(char[] source, int sourceOffset, int sourceCount, String target, int fromIndex) - 返回 source 中 target 在 fromIndex 开始处首次出现的索引位置,如果 target 不在 source 中则返回 -1

  • String.indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) - 返回 source 中 target 在 fromIndex 开始处首次出现的索引位置,如果 target 不在 source 中则返回 -1

  • String.lastIndexOf(char[] source, int sourceOffset, int sourceCount, String target, int fromIndex) - 返回 source 中 target 在 fromIndex 开始处最后一次出现的索引位置,如果 target 不在 source 中则返回 -1

  • String.lastIndexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) - 返回 source 中 target 在 fromIndex 开始处最后一次出现的索引位置,如果 target 不在 source 中则返回 -1

  • String.format(String format, Object... args) - 返回格式化后的字符串,语言环境由当前线程的默认 Locale 决定

    格式 format 包括 普通文本 和 格式说明符(如 %s%d%.2f%n)。

    变参列表 args 则用于替换格式字符串中的格式说明符。

  • String.format(Locale l, String format, Object... args) - 返回格式化后的字符串,语言环境由参数 l 决定

  • String.valueOf(Object obj) - 返回对象的字符串化结果

    其实就是调用 obj.toString() 方法。

    如果 obj 为 null,则返回 "null" 字符串。

  • String.valueOf(char data[]) - 返回 char[] 数组构建的新字符串

  • String.valueOf(char data[], int offset, int count) - 返回 char[] 数组的部分元素构建的新字符串

  • String.copyValueOf(char data[], int offset, int count) - 返回 char[] 数组的部分元素构建的新字符串

    String.valueOf(char data[], int offset, int count) 方法的实现一模一样。

  • String.copyValueOf(char data[]) - 返回 char[] 数组的构建的新字符串

    String.valueOf(char data[]) 方法的实现一模一样。

  • String.valueOf(boolean b) - 返回布尔值 b 的字符串化结果

    如果 b 为 true,则返回 "true" 字符串,否则返回 "false" 字符串。

  • String.valueOf(char c) - 返回字符 c 的字符串化结果

  • String.valueOf(int i) - 返回整数 i 的字符串化结果

    其实就是调用 Integer.toString(i) 并返回其结果。

  • String.valueOf(long l) - 返回长整数 l 的字符串化结果

  • String.valueOf(float f) - 返回浮点数 f 的字符串化结果

  • String.valueOf(double d) - 返回双精度浮点数 d 的字符串化结果

实例方法

  • String.instance.length() - 返回当前字符串长度(字节的个数)

  • String.instance.isEmpty() - 判断当前字符串是否为空,如果当前字符串长度为 0 则返回 true,否则返回 false

  • String.instance.charAt(int index) - 返回当前字符串中指定索引位置的字符,索引位置从 0 开始

  • String.instance.codePointAt(int index) - 返回当前字符串中指定索引位置的 Unicode 编码(int),索引位置从 0 开始

    我们知道,非 ASCII 字符(后面简称字符)要显示到屏幕上,是需要先找到字符在字符矩阵中的位置,然后再显示该字符。字符在矩阵中的坐标,就叫代理对。字符在内存中一般需要两个连续的字节来表示该代理对,其中高字节叫高代理,低字节叫低代理。代理对与 Unicode 编码存在一对一的关系。

    codePointAt 方法(以及下方的 codePointBefore 方法)为我们处理了非 ASCII 字符代理对与 Unicode 编码的映射关系,从而返回正确的代码点。

  • String.instance.codePointBefore(int index) - 返回当前字符串中指定索引位置的前一个字符的 Unicode 编码(int),索引位置从 0 开始

  • String.instance.codePointCount(int beginIndex, int endIndex) - 返回当前字符串中指定索引位置区间内(包括起始索引,不包括结束索引)的字符数量,索引位置从 0 开始

    本方法可获得字符串的真正长度。

    索引在这里指的是字节索引。如果索引刚好落在低代理,则低代理前面的高代理也会被视作一个字符而被统计。

  • String.instance.offsetByCodePoints(int index, int codePointOffset) - 返回当前字符串中指定索引偏移 codePointOffset 后的字符索引位置

    如果 codePointOffset 是正数,则向后移动;如果是负数,则向前移动。

    index 表示的是字节索引。而 codePointOffset 指的是字符(代码点)数,而不是字节数。

    如果 codePointOffset 是整数,起始点 index 刚好位于低代理,则低代理将会作为一个字符单独计算;如果 codePointOffset 是负数,起始点 index 刚好位于高代理,则高代理将会作为一个字符单独计算。

  • String.instance.getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) - 将当前字符串中指定索引区间(包括起始索引,不包括结束索引)的字符复制到目标数组中,索引位置从 0 开始

    其中 srcBegin 和 srcEnd 表示的均是字符索引,而非字节索引,这个必须牢记。

    String s = "Hello, 世界";
    char[] chs = new char[5];
    
    s.getChars(7, 9, chs, 0);
    System.out.println(chs);    // 输出: 世界 null null null
    
  • String.instance.getBytes(String charsetName) - 将当前字符串以指定的字符集编码(String)的方式转换为字节数组,并返回该字节数组

  • String.instance.getBytes(Charset charset) - 将当前字符串以指定的字符集编码(Charset)的方式转换为字节数组,并返回该字节数组

  • String.instance.getBytes() - 将当前字符串以当前平台默认字符集编码(例如 UTF-8)的方式转换为字节数组,并返回该字节数组

  • String.instance.equals(Object anObject) - 判断当前字符串的所有字符与给定的字符串 anObject 对象的是否相等,相等返回 true,否则返回 false

    本方法只能比较两个 String 对象,不能比较 String 和 StringBuffer、String 和 StringBuilder 等。

  • String.instance.contentEquals(StringBuffer sb) - 判断当前字符串的所有字符与给定的字符串缓冲区 sb 对象的是否相等,相等返回 true,否则返回 false

    本方法只能用于单线程,多线程请使用 nonSyncContentEquals 方法。

  • String.instance.nonSyncContentEquals(AbstractStringBuilder sb) - 判断当前字符串的所有字符与给定的字符串缓冲区 sb 对象的是否相等,相等返回 true,否则返回 false

  • String.instance.contentEquals(CharSequence cs) - 判断当前字符串的所有字符与给定的字符序列 cs 对象的是否相等,相等返回 true,否则返回 false

  • String.instance.equalsIgnoreCase(String anotherString) - 判断当前字符串的所有字符与给定的字符串 anotherString 对象的是否相等(忽略大小写),相等返回 true,否则返回 false

  • String.instance.compareTo(String anotherString) - 比较当前字符串与给定的字符串 anotherString 对象的大小,如果当前字符串小于 anotherString 对象,则返回 -1;如果当前字符串大于 anotherString 对象,则返回 1;如果当前字符串等于 anotherString 对象,则返回 0

    算法的基本原理是:相同长度的部分,采用 this.charAt(k) - anotherString.charAt(k),如果比较结果为 0,再采用 this.length() - anotherString.length(),返回最终结果

  • String.instance.compareToIgnoreCase(String str) - 比较当前字符串与给定的字符串 str 对象的大小(忽略大小写),如果当前字符串小于 str 对象,则返回 -1;如果当前字符串大于 str 对象,则返回 1;如果当前字符串等于 str 对象,则返回 0

  • String.instance.regionMatches(int toffset, String other, int ooffset, int len) - 判断当前字符串从 toffset 位置开始与给定的字符串 other 从 ooffset 位置开始,长度为 len 的子字符串是否相等,相等返回 true,否则返回 false

  • String.instance.regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) - 判断当前字符串从 toffset 位置开始与给定的字符串 other 从 ooffset 位置开始,长度为 len 的子字符串是否相等(忽略大小写),相等返回 true,否则返回 false

  • String.instance.startsWith(String prefix, int toffset) - 判断当前字符串从 toffset 位置开始是否以给定的字符串 prefix 开头,是则返回 true,否则返回 false

  • String.instance.startsWith(String prefix) - 判断当前字符串是否以给定的字符串 prefix 开头,是则返回 true,否则返回 false

  • String.instance.endsWith(String suffix) - 判断当前字符串是否以给定的字符串 suffix 结尾,是则返回 true,否则返回 false

  • String.instance.hashCode() - 返回当前字符串的哈希码 (int)

    哈希码的计算规则是: s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1],其中 s[i] 是当前字符编码,31 是 32 (int 就是 32 位)这个数字中最大质数,n 是字符串长度。

  • String.instance.indexOf(int ch) - 返回当前字符串中第一次出现给定字符 ch 的索引位置,如果当前字符串中不存在给定字符 ch,则返回 -1

  • String.instance.indexOf(int ch, int fromIndex) - 返回当前字符串中第一次出现给定字符 ch 的索引位置,如果当前字符串中不存在给定字符 ch,则返回 -1,索引位置从 fromIndex 开始

  • String.instance.lastIndexOf(int ch) - 返回当前字符串中最后一次出现给定字符 ch 的索引位置,如果当前字符串中不存在给定字符 ch,则返回 -1

  • String.instance.lastIndexOf(int ch, int fromIndex) - 返回当前字符串中最后一次出现给定字符 ch 的索引位置,如果当前字符串中不存在给定字符 ch,则返回 -1,索引位置从 fromIndex 开始倒数

  • String.instance.indexOf(String str) - 返回当前字符串中第一次出现给定字符串 str 的索引位置,如果当前字符串中不存在给定字符串 str,则返回 -1

  • String.instance.indexOf(String str, int fromIndex) - 返回当前字符串中第一次出现给定字符串 str 的索引位置,如果当前字符串中不存在给定字符串 str,则返回 -1,索引位置从 fromIndex 开始

  • String.instance.lastIndexOf(String str) - 返回当前字符串中最后一次出现给定字符串 str 的索引位置,如果当前字符串中不存在给定字符串 str,则返回 -1

  • String.instance.lastIndexOf(String str, int fromIndex) - 返回当前字符串中最后一次出现给定字符串 str 的索引位置,如果当前字符串中不存在给定字符串 str,则返回 -1,索引位置从 fromIndex 开始倒数

  • String.instance.substring(int beginIndex) - 返回当前字符串从 beginIndex 开始到结尾的子字符串

  • String.instance.substring(int beginIndex, int endIndex) - 返回当前字符串从 beginIndex 开始(包含)到 endIndex 结束(不包含)的子字符串

  • String.instance.subSequence(int beginIndex, int endIndex) - 返回当前字符串从 beginIndex 开始(包含)到 endIndex 结束(不包含)的子序列

    subSequence(int beginIndex, int endIndex) 其实就是直接返回 substring(int beginIndex, int endIndex) 的结果

  • String.instance.concat(String str) - 返回当前字符串与给定字符串 str 连接后的新字符串

  • String.instance.replace(char oldChar, char newChar) - 返回当前字符串中给定字符 oldChar 替换为 newChar 后的新字符串

    如果 oldChar 与 newChar 相等,则返回当前字符串。

  • String.instance.matches(String regex) - 判断当前字符串是否与给定正则表达式 regex 匹配,匹配返回 true,否则返回 false

    本方法其实就是直接返回 Pattern.matches(String regex, CharSequence input) 的判断结果,input 在这里使用 this 替代。

  • String.instance.contains(CharSequence s) - 判断当前字符串是否包含给定字符串 s,包含返回 true,否则返回 false

  • String.instance.replaceFirst(String regex, String replacement) - 返回当前字符串中第一次匹配给定正则表达式 regex 的子字符串替换为 replacement 后的新字符串

  • String.instance.replaceAll(String regex, String replacement) - 返回当前字符串中全部匹配给定正则表达式 regex 的子字符串替换为 replacement 后的新字符串

  • String.instance.replace(CharSequence target, CharSequence replacement) - 返回当前字符串中全部匹配给定字符串 target 的子字符串替换为 replacement 后的新字符串

  • String.instance.split(String regex, int limit) - 使用当前字符串分割为最多 limit 个字符串,分隔符为 regex 正则表达式,返回分割后的字符串数组

    如果 limit 小于等于 0,则不限制分割后的字符串数量。

    如果 limit 小于或大于 0,会保留两侧的空字符串;如果 limit 等于 0,结果集会舍弃两侧的空字符串。

  • String.instance.split(String regex) - 使用当前字符串分割为字符串数组,分隔符为 regex 正则表达式,返回分割后的字符串数组

  • String.instance.join(CharSequence delimiter, CharSequence... elements) - 返回给定分隔符连接多个字符序列后的新字符串

  • String.instance.join(CharSequence delimiter, Iterable<? extends CharSequence> elements) - 返回给定分隔符连接可迭代序列各个元素后的新字符串

    可迭代序列包括 数组、集合、列表 等。

  • String.instance.toLowerCase(Locale locale) - 返回当前字符串全部 locale 语言字符转换为小写后的新字符串

    如果当前字符串不需要转换为小写,则返回当前字符串。

  • String.instance.toLowerCase() - 返回当前字符串全部当前默认语言字符转换为小写后的新字符串

    其实就是直接返回 toLowerCase(Locale.getDefault()) 的结果。

  • String.instance.toUpperCase(Locale locale) - 返回当前字符串全部 locale 语言字符转换为大写后的新字符串

  • String.instance.toUpperCase() - 返回当前字符串全部当前默认语言字符转换为大写后的新字符串

    其实就是直接返回 toUpperCase(Locale.getDefault()) 的结果。

  • String.instance.trim() - 返回当前字符串去除两侧的空白字符后的新字符串

    空白字符的定义是字符编码小于等于空格符的字符。

    如果当前字符串不需要去除两侧的空白字符,则返回当前字符串。

  • String.instance.toString() - 返回当前字符串对象自身

  • String.instance.toCharArray() - 返回一个字符数组,数组复制自当前字符串

  • String.instance.intern() - 池化当前字符串,并返回在常量池中的新引用

    如果当前字符串已经存在于常量池中,则直接返回常量池中的引用。

    当程序中有大量重复的字符串时,使用 String.instance.intern() 可以减少内存占用:

    String str1 = new String("Hello World");
    String str2 = str1.intern();                // 将 str1 的字符串内容池化(添加到字符串常量池中)
    String str3 = "Hello World";                // JVM 直接从字符串常量池中获取
    
    System.out.println(str1 == str2);           // false
    System.out.println(str2 == str3);           // true
    

小结

综上,我们可以使用 String 的静态方法来将其他数据类型转换为字符串、实现字符串的子串处理(包括查找、拼接、格式化),也可以使用 String 的实例方法来实现更多的操作包括但不限于编码转换、字符判断、正则匹配、分割拼接。