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