写在前面
说说这几天看源码的感受吧,其实 jdk 中的源码设计是最值得进阶学习的地方。我们在对 api 较为熟悉之后,完全可以去尝试阅读一些 jdk 源码,打开 jdk 源码后,如果你英文能力稍微过得去,那么源码有相当详细的注释告诉你 api 的含义,具体用法。假设平时在写代码的过程中突然忘记了某个 api 的用法,那么有些新手没读过源码的可能顺手就打开百度或者谷歌,搜索 api 怎么用?哈哈哈,面向谷歌编程,这样的状态可能会让你一年的经验重复 n 年,如果是阅读过源码,则直接进去看看源码英文注释,回想一下源码的实现即可使用,而且看过源码后,里面有些代码细节是可以在平时编码的过程中直接借鉴的。
废话有点多啦~~滴滴滴,上车了。。。
上一篇 String 源码浅析 (一) 中已经对 String 前半部分源码做了解析,这篇把剩下的方法粗略的总结下 …
String 成员方法
判断字符串是否相等,该方法继承自 Object 类的重写实现,原则上也是比较字符串中的字符是否相等。
public boolean equals(Object anObject) {
// 判断形参跟当前字符串对象地址是否相等,即是否为同一个对象,如果相等,则返回 true
if (this == anObject) {
return true;
}
// 如果形参为 String 类型对象
if (anObject instanceof String) {
// 强转为 String 类型对象
String anotherString = (String)anObject;
// 当前字符串对象的字符数组长度
int n = value.length;
// 如果当前字符串对象的字符数组长度等于形参字符串字符数组长度
if (n == anotherString.value.length) {
// 当前字符串字符数组
char v1[] = value;
// 形参字符串字符数组
char v2[] = anotherString.value;
// 遍历索引起始位置 0
int i = 0;
// 遍历当前字符串字符数组,每个索引位置的字符与形参字符串索引位置字符比较,如果不相等则返回 false
while (n– != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
// 以上条件都不满足,最后返回 false
return false;
}
传入 CharSequence 接口形参,实际是与 StringBuffer,StringBuilder 比较是否相等,因为 StringBuffer,StringBuilder 都实现了 CharSequence 接口
public boolean contentEquals(CharSequence cs) {
// 判断形参是否是 AbstractStringBuilder 抽象类,实则因当传入的是其子类:StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {
// 如果形参是 StringBuffer 类型对象
if (cs instanceof StringBuffer) {
// 同步锁,调用 nonSyncContentEquals 方法比较两种是否相等
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else {
// 如果形参对象是 StringBuilder,则调用 nonSyncContentEquals 方法比较两种是否相等
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
// 如果形参是 String 对象,则直接调用 equals 方法返回
if (cs instanceof String) {
return equals(cs);
}
// 如果是其他的 CharSequence 实现类,则遍历,一个个字符进行比较,找到一个字符不相等则直接返回 false
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;
}
}
// 以上代码都不成立,走到最后直接返回 true
return true;
}
私有方法,非同步方式(线程不安全)比较与 AbstractStringBuilder 是否相等,实则是与其子类:StringBuffer, StringBuilder 比较大小,contentEquals(CharSequence cs) 方法中核心比较代码就是调用该方法。
private boolean nonSyncContentEquals(AbstractStringBuilder sb) {
// 当前字符串对象字符数组
char v1[] = value;
// 获取形参字符数组
char v2[] = sb.getValue();
// 当前字符串对象字符数组长度
int n = v1.length;
// 如果当前字符串对象字符数组长度不等于形参字符数组长度,则直接返回 false
if (n != sb.length()) {
return false;
}
// 遍历当前字符串对象字符数组,与形参字符数组逐一比较字符,找到一个字符不相等,则直接返回 false
for (int i = 0; i < n; i++) {
if (v1[i] != v2[i]) {
return false;
}
}
// 以上条件都不成立,代码走到最后则直接返回 true
return true;
}
公有方法,比较与 StringBuffer 对象是否相等,内部实则直接调用的 contentEquals(CharSequence cs) 方法,可以说该方法是 StringBuffer 的特别版吧。
public boolean contentEquals(StringBuffer sb) {
return contentEquals((CharSequence)sb);
}
匹配两个字符串部分片段是否相等
public boolean regionMatches(int toffset, String other, int ooffset,
int len) {
// 当前字符串字符数组
char ta[] = value;
// 当前字符串开始比较的起始位置,即偏移量
int to = toffset;
// 待比较的字符串字符数组
char pa[] = other.value;
// 待比较的字符串起始位置,即偏移量
int po = ooffset;
// 索引检查 1. 偏移量小于 0 2. 偏移量大于总长度 - 待比较的长度
// 以上两种情况直接返回 false
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)value.length – len)
|| (ooffset > (long)other.value.length – len)) {
return false;
}
// 遍历,找出不相等的字符,则返回 false
while (len– > 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
// 不出意外,最终则返回 true
return true;
}
匹配两个字符串部分片段是否相等,同时判断是否忽略大小写
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;
// 索引检查 1. 偏移量小于 0 2. 偏移量大于总长度 - 待比较的长度
// 以上两种情况直接返回 false
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) {
// 字符转换为大写
char u1 = Character.toUpperCase(c1);
char u2 = Character.toUpperCase(c2);
// 如果相等,则继续跳过
if (u1 == u2) {
continue;
}
// 转换为小写进行比较,如果相等则继续跳过
if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
continue;
}
}
// 否则发现不相等,则直接返回 false
return false;
}
// 不出意外,最终返回 true
return true;
}
忽略大小写比较字符串大小
public boolean equalsIgnoreCase(String anotherString) {
// 一句三目运算直接搞定
// 如果当前字符串对象地址与形参字符串相等,则返回 true
// 否则判断形参字符串是否为空,形参字符串长度是否与当前字符串长度相等,直接调用 regionMatches 比较两个字符串所有字符是否相等,同时忽略大小写比较
// 以上全部为 true 则相等
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.value.length == value.length)
&& regionMatches(true, 0, anotherString, 0, value.length);
}
比较两个字符串是否相等,该方法实现自 Comparable 接口,返回 int 结果,等于 0,则字符串相等,小于 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;
// 索引位置 0 开始
int k = 0;
// 遍历较小的字符数组
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
// 如果字符不相等
if (c1 != c2) {
// 返回字符之差
return c1 – c2;
}
k++;
}
// 如果字符都相等,则返回长度之差
return len1 – len2;
}
使用默认比较器不区分大小写比较两个字符串大小
// 初始化默认的比较器
public static final Comparator<String> CASE_INSENSITIVE_ORDER
= new CaseInsensitiveComparator();
// 默认的比较器,不区分大小写
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = 8575799808933029326L;
public int compare(String s1, String s2) {
// 第一个字符串长度
int n1 = s1.length();
// 第二个字符串长度
int n2 = s2.length();
// 取小
int min = Math.min(n1, n2);
// 遍历较小的字符串
for (int i = 0; i < min; i++) {
// 获取指定索引字符
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
// 如果字符不相等
if (c1 != c2) {
// 转化为大写
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
// 转化为大写后比较,不相等
if (c1 != c2) {
// 转化为小写继续比较
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
// 转化为小写字符,不相等
if (c1 != c2) {
// 直接返回字符之差
return c1 – c2;
}
}
}
}
// 不出意外,最终返回长度之差
return n1 – n2;
}
/** Replaces the de-serialized object. */
private Object readResolve() { return CASE_INSENSITIVE_ORDER;}
}
// 内部直接调用默认比较器的 compare 方法
public int compareToIgnoreCase(String str) {
return CASE_INSENSITIVE_ORDER.compare(this, str);
}
从指定偏移量开始,判断是否以指定字符串开头
public boolean startsWith(String prefix, int toffset) {
// 当前字符串字符数组
char ta[] = value;
// 偏移量
int to = toffset;
// 指定字符串前缀字符数组
char pa[] = prefix.value;
// 索引位置 0 开始
int po = 0;
// 指定字符串前缀数组长度
int pc = prefix.value.length;
// 偏移量小于 0 或者 // 偏移量大于总长度 - 字符串前缀长度,则直接返回 false
if ((toffset < 0) || (toffset > value.length – pc)) {
return false;
}
// 遍历前缀字符串
while (–pc >= 0) {
// 从偏移量开始检索,找到字符不相等,则返回 false
if (ta[to++] != pa[po++]) {
return false;
}
}
// 不出意外,最后则返回 true
return true;
}
从字符串开头,判断是否以指定字符串开头
public boolean startsWith(String prefix) {
// 直接调用 startsWith 重载方法, 偏移量为 0
return startsWith(prefix, 0);
}
判断是否以指定字符串结尾, 内部直接调用的 startsWith 方法,偏移量为总字符串长度 - 后缀字符串长度即可。
public boolean endsWith(String suffix) {
return startsWith(suffix, value.length – suffix.value.length);
}
从指定偏移量开始,搜索指定字符在字符串中第一次出现的索引位置
public int indexOf(int ch, int fromIndex) {
// 当前字符串字符数组长度
final int max = value.length;
// 如果偏移量小于 0,则重置为 0
if (fromIndex < 0) {
fromIndex = 0;
} else if (fromIndex >= max) {
// 偏移量大于总长度,则返回 -1,意味着找不到指定字符
return -1;
}
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
// 当前字符串字符数组
final char[] value = this.value;
// 从偏移量位置开始遍历
for (int i = fromIndex; i < max; i++) {
// 找到相等的字符,则返回索引
if (value[i] == ch) {
return i;
}
}
// 找不到则返回 -1
return -1;
} else {
return indexOfSupplementary(ch, fromIndex);
}
}
查找指定字符在字符串中第一次出现的索引位置,从偏移量 0 开始遍历查找
public int indexOf(int ch) {
return indexOf(ch, 0);
}
查找指定字符在字符串中最后一次出现的索引位置,从指定偏移量开始遍历
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;
}
}
// 找不到则返回 -1
return -1;
} else {
return lastIndexOfSupplementary(ch, fromIndex);
}
}
查找指定字符在字符串中最后一次出现的索引位置,从字符串最后一个索引位置开始遍历查找
public int lastIndexOf(int ch) {
return lastIndexOf(ch, value.length – 1);
}
从指定位置开始,查找字符串在源字符串中第一次出现的的索引位置,内部实则直接调用的 indexOf 内部静态方法
public int indexOf(String str, int fromIndex) {
return indexOf(value, 0, value.length,
str.value, 0, str.value.length, fromIndex);
}
查找字符串在源字符串中第一次出现的的索引位置,内部实则直接调用的上述 indexOf 方法,fromIndex 默认从 0 开始
public int indexOf(String str) {
return indexOf(str, 0);
}
从指定位置开始,查找字符串在源字符串中最后一次出现的的索引位置,内部实则直接调用的 lastIndexOf 内部静态方法
public int lastIndexOf(String str, int fromIndex) {
return lastIndexOf(value, 0, value.length,
str.value, 0, str.value.length, fromIndex);
}
查找字符串在源字符串中最后一次出现的的索引位置,内部实则直接调用的上述 lastIndexOf 方法,fromIndex 默认从 value.length 开始
public int lastIndexOf(String str) {
return lastIndexOf(str, value.length);
}
按照指定区间裁剪字符串,返回子字符串,beginIndex 起始位置 (包含),endIndex 结束位置 (不包含)
public String substring(int beginIndex, int endIndex) {
// 起始位置小于 0,抛出索引越界异常
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
// 结束位置大于字符串总长度,则抛出索引越界异常
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
// 待截取的字符串长度
int subLen = endIndex – beginIndex;
// 待截取的字符串长度小于 0,则抛出索引越界异常
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
// 三目判断 起始位置等于 0,并且结束位置等于字符串长度,则表示为截取总长度,返回当前字符串对象
// 否则重新 new 一个字符串实例,从 beginIndex 开始,截取 subLen 长度
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
从起始位置 beginIndex 开始截取源字符串到结尾,返回子字符串
public String substring(int beginIndex) {
// 起始位置小于 0,抛出索引越界异常
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
// 待截取的字符串长度
int subLen = value.length – beginIndex;
// 待截取的字符串长度小于 0,则抛出索引越界异常
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
// 三目判断 起始位置等于 0,则表示为截取总长度,返回当前字符串对象,否则重新 new 一个新的字符串实例,从 beginIndex 开始,截取 subLen 长度
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
按照指定区间裁剪字符串,返回 CharSequence 接口,beginIndex 起始位置 (包含),endIndex 结束位置 (不包含),内部实则直接调用的 substring 方法,只是返回的是最上层接口罢了
public CharSequence subSequence(int beginIndex, int endIndex) {
return this.substring(beginIndex, endIndex);
}
字符串拼接,将目标字符串拼接在尾部,返回新的字符串
public String concat(String str) {
// 待拼接的字符串长度
int otherLen = str.length();
// 如果待拼接的字符串长度等于 0,则直接返回当前字符串对象
if (otherLen == 0) {
return this;
}
// 当前字符串长度
int len = value.length;
// 将当前字符串字符数组拷贝到新的字符数组 buf[] 中,长度扩容至 len + otherLen
char buf[] = Arrays.copyOf(value, len + otherLen);
// 将待拼接的字符数组拷贝至 buf[] 中,从 len 开始,理论上这部操作完之后,字符数组已经拼接成功了
str.getChars(buf, len);
// 利用最新的字符数组,重新 new 一个新的字符串实例返回
return new String(buf, true);
}
将字符串中指定字符 oldChar 替换为新的字符 newChar
public String replace(char oldChar, char newChar) {
// 如果旧的字符跟新的字符不相等,才执行替换逻辑,否则直接返回当前字符串对象
if (oldChar != newChar) {
// 当前字符串长度
int len = value.length;
// 索引从 - 1 开始
int i = -1;
// 当前字符串字符数组
char[] val = value;
// 遍历当前字符数组,从 0 开始,因为 ++ i 之后等于 0
while (++i < len) {
// 找到与 oldChar 相等的字符,则中断循环
if (val[i] == oldChar) {
break;
}
}
// 如果 oldChar 索引位置 i 小于当前字符数组长度,意味着在当前字符串中找到了 oldChar
if (i < len) {
// 初始化字符串长度的字符数组
char buf[] = new char[len];
// 从 0 开始遍历到 oldChar 所在位置,将字符放入 buf
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
// 从 oldChar 索引位置 i 开始遍历到字符串结尾
while (i < len) {
// 当前字符
char c = val[i];
// 如果当前字符等于 oldChar, 则 newChar 赋值给 buf[i],否则不变
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
// 最后根据 buf 数组重新 new 一个新的字符串实例返回
return new String(buf, true);
}
}
return this;
}
根据字符串正则 regex 匹配字符串,返回 boolean,内部实则是调用正则匹配的 api 方法
public boolean matches(String regex) {
return Pattern.matches(regex, this);
}
判断字符串是否包含指定字符序列 CharSequence, 内部实则是调用 indexOf 方法,判断返回结果是否大于 -1
public boolean contains(CharSequence s) {
return indexOf(s.toString()) > -1;
}
根据给定的新的子字符串 replacement,替换第一个匹配给定的正则表达式 regex 的子字符串,内部实则调用的是 Matcher 类的 replaceFirst 方法
public String replaceFirst(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
}
根据给定的新的子字符串 replacement,替换所有匹配给定的正则表达式 regex 的子字符串,内部实则调用的是 Matcher 类的 replaceAll 方法
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
更加通用的字符串替换方法,将匹配到的 target 字符序列全部替换为 replacement 字符序列,内部调用的也是 Matcher 类的 replaceAll 方法
public String replace(CharSequence target, CharSequence replacement) {
return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}
去除字符串前后空格
public String trim() {
// 当前字符串长度
int len = value.length;
// 索引标志位
int st = 0;
// 当前字符串字符数组
char[] val = value;
// 从 0 开始遍历循环,查找到空格的字符,则索引往前 +1
while ((st < len) && (val[st] <= ‘ ‘)) {
st++;
}
// 从字符串尾部开始遍历循环,查找到空格字符,则长度往后 -1
while ((st < len) && (val[len – 1] <= ‘ ‘)) {
len–;
}
// 如果 st 索引大于 0 或者长度 len 小于总长度,则返回裁剪字符串 st 偏移量开始,裁剪 len 长度,否则直接返回当前字符串对象
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
字符串转化 toString,继承自 Object 重写的方法,直接返回当前字符串对象
public String toString() {
return this;
}
字符串转化为字符数组
public char[] toCharArray() {
// 初始化字符串长度的字符数组
char result[] = new char[value.length];
// 将字符串本身的字符数组拷贝至 result[] 并返回结果
System.arraycopy(value, 0, result, 0, value.length);
return result;
}
String 静态方法
不对外公开的内部静态方法,从字符串 source 指定索引 fromIndex 开始遍历,从偏移量 sourceOffset,在原始字符串长度 sourceCount 范围内查找目标字符串 target 中偏移量 targetOffset 开始,长度为 targetCount 的字符串索引第一次出现的位置。
static int indexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex) {
// 如果起始位置大于等于源字符串指定长度
if (fromIndex >= sourceCount) {
// 如果目标字符串查找的长度为 0,则直接返回源字符串长度,否则返回 - 1 表示未找到
return (targetCount == 0 ? sourceCount : -1);
}
// 起始位置小于 0
if (fromIndex < 0) {
// 重置为 0
fromIndex = 0;
}
// 目标字符串长度为 0,代表为空字符串”“
if (targetCount == 0) {
// 直接返回起始位置
return fromIndex;
}
// 目标字符串第一个字符
char first = target[targetOffset];
// 从源字符偏移量开始,计算最大的遍历次数
int max = sourceOffset + (sourceCount – targetCount);
// 遍历
for (int i = sourceOffset + fromIndex; i <= max; i++) {
// 循环找出第一个字符
if (source[i] != first) {
while (++i <= max && source[i] != first);
}
//todo 这段暂时没看明白 by zhangshaolin
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;
}
}
}
// 最终没找到 则返回 -1
return -1;
}
不对外公开的静态方法, 上述方法的另一个重载形式,内部实则直接调用的上述方法,targetCount 默认传入 target.value.length
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);
}
不对外公开的内部静态方法,从字符串 source 指定索引 fromIndex 开始遍历,从偏移量 sourceOffset,在原始字符串长度 sourceCount 范围内查找目标字符串 target 中偏移量 targetOffset 开始,长度为 targetCount 的字符串索引第一次出现的位置。
static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex) {
// 源字符长度 - 目标字符长度,获取起始位置
int rightIndex = sourceCount – targetCount;
// 起始位置小于 0,直接返回 - 1 表示未找到目标
if (fromIndex < 0) {
return -1;
}
// 如果形参起始位置大于计算的实际起始位置,则直接赋值给 fromIndex
if (fromIndex > rightIndex) {
fromIndex = rightIndex;
}
// 如果目标字符串长度为 0,表示为空字符串,则直接返回起始位置
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–;
}
// 如果 i <min,则代表查找不到最后一个字符 返回 -1
if (i < min) {
return -1;
}
//todo 这段逻辑暂时没看明白 by zhangshaolin
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;
}
}
不对外公开的静态方法, 上述方法的另一个重载形式,内部实则直接调用的上述方法,targetCount 默认传入 target.value.length
static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,
String target, int fromIndex) {
return lastIndexOf(source, sourceOffset, sourceCount,
target.value, 0, target.value.length,
fromIndex);
}
将任意 Object 对象转化为字符串对象返回,内部实则也是调用的 Object 的 toString 方法,返回结果取决于该方法的具体实现
public static String valueOf(Object obj) {
// 如果 obj 为空,则返回 ”null”, 否则返回对象的 toString 返回的字符串结果
return (obj == null) ? “null” : obj.toString();
}
将字符数组转化为字符串对象返回,内部实际是用字符数组为参数 重新 new 了一个新的字符串对象,并返回
public static String valueOf(char data[]) {
return new String(data);
}
将字符数组转化为字符串对象返回,同时指定索引偏移量 offset,截取的长度 count,内部实际是重新 new 了一个新的字符串对象,并返回
public static String valueOf(char data[], int offset, int count) {
return new String(data, offset, count);
}
与上一个方法效果一致,只是方法名称不同罢了
public static String copyValueOf(char data[], int offset, int count) {
return new String(data, offset, count);
}
与 valueOf(char data[]) 效果一致,只是方法名称不同罢了
public static String copyValueOf(char data[]) {
return new String(data);
}
将 boolean 类型数据转化为字符串对象返回
public static String valueOf(boolean b) {
// 为真 则返回 ”true” 否则返回 ”false”
return b ? “true” : “false”;
}
将字符转化为字符串对象返回
public static String valueOf(char c) {
// 初始化字符数组
char data[] = {c};
// 重新 new 一个新的字符串对象返回
return new String(data, true);
}
将 int 数据转换为字符串对象返回,内部实际是调用的 Integer.toString() 方法
public static String valueOf(int i) {
return Integer.toString(i);
}
将 long 数据转换为字符串对象返回,内部实际是调用的 Long.toString() 方法
public static String valueOf(long l) {
return Long.toString(l);
}
将 float 数据转换为字符串对象返回,内部实际是调用的 Float.toString() 方法
public static String valueOf(float f) {
return Float.toString(f);
}
将 double 数据转换为字符串对象返回,内部实际是调用的 Double.toString() 方法
public static String valueOf(double d) {
return Double.toString(d);
}
简单总结
把 String 源码全部大致过了一遍之后,感慨 jdk 代码设计的强大,几天时间要完全看懂是不容易的,目前也还有很多地方没有完全明白
源码并不可怕,可怕的是自己的畏惧心理,认为源码很难啃不动,其实不然,下定决心看下去,遇到不懂的可以先 pass, 后面再回头看可能就豁然开朗了。
String 内部本质就是操作字符数组 value[]
因为本质就是操作字符数组,内部用到了大量的 Arrays.copyOf, 以及 System.arraycopy 方法
最后
看源码不易,如果文中有错误之处,还请留言指出,一起学习,一起进步,谢谢!
更多原创文章会第一时间推送公众号【张少林同学】,欢迎关注!