jdk源码解析--String 类
Posted 我的IT技术路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jdk源码解析--String 类相关的知识,希望对你有一定的参考价值。
在Java语言中,字符串有着较多的函数,功能实现也比较齐全。看下jdk1.8中String类的具体实现。
1.1 这个类的基本定义如下:
1. public final class String//一个不能被继承的类
2. implements java.io.Serializable, Comparable<String>, CharSequence {
4. private int hash; // 保存hash值,默认为0
5. private static final long serialVersionUID = -6849794470754667710L;//序列化uuid
6. private static final ObjectStreamField[] serialPersistentFields =
7. new ObjectStreamField[0]; //保存具体的Objectstream的值
8. }
1.2 构造函数:
由于String类的构造函数比较多,这里选择两个有代表性的进行解析,其他的相对简单,请读者自行观看源码。
1. public String(char value[], int offset, int count) {
2. if (offset < 0) {
3. throw new StringIndexOutOfBoundsException(offset);
4. }
5. if (count <= 0) {
6. if (count < 0) {
7. throw new StringIndexOutOfBoundsException(count);
8. }
9. if (offset <= value.length) {
10. this.value = "".value;
11. return;
12. }
13. }
14. // Note: offset or count might be near -1>>>1.
15. if (offset > value.length - count) {
16. throw new StringIndexOutOfBoundsException(offset + count);
17. } //以上都是参数校验,判断输入的字符数组中是否包含要截取的字符长度
18. this.value = Arrays.copyOfRange(value, offset, offset+count); //调用部分拷贝功能进行赋值
19. }
//可以输入一个整数数组,将其转换为对应的ASCII码的字符串
1. public String(int[] codePoints, int offset, int count) {
2. if (offset < 0) {
3. throw new StringIndexOutOfBoundsException(offset);
4. }
5. if (count <= 0) {
6. if (count < 0) {
7. throw new StringIndexOutOfBoundsException(count);
8. }
9. if (offset <= codePoints.length) {
10. this.value = "".value;
11. return;
12. }
13. }
14. // Note: offset or count might be near -1>>>1.
15. if (offset > codePoints.length - count) {
16. throw new StringIndexOutOfBoundsException(offset + count);
17. }
18.
19. final int end = offset + count;
20.
21. // 统计准确的字符个数,因为有些整数转换成字符,就不止一个字符大小,如果有超出范围的就抛出异常
22. int n = count;
23. for (int i = offset; i < end; i++) {
24. int c = codePoints[i];
25. if (Character.isBmpCodePoint(c)) //判断是否是bmp码,基本的字符编码
26. continue;
27. else if (Character.isValidCodePoint(c)) /判断是否是有效的code码
28. n++;
29. else throw new IllegalArgumentException(Integer.toString(c));
30. }
31.
32. // Pass 2: Allocate and fill in char[]
33. final char[] v = new char[n];
34. //转换成字符串,如果是bmp码就占用1个字符,两个字节,如果不是占用两个字符,4个字节
35. for (int i = offset, j = 0; i < end; i++, j++) {
36. int c = codePoints[i];
37. if (Character.isBmpCodePoint(c))
38. v[j] = (char)c;
39. else
40. Character.toSurrogates(c, v, j++);
41. }
42.
43. this.value = v;
44. }
//将stringbuffer转换成str
1. public String(StringBuffer buffer) {
2. synchronized(buffer) {//由于strbuffer线程安全,所以类加上锁
3. this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
4. }
5. }
//stringbuilder 转换为string,使用了字符数组 拷贝函数
1. public String(StringBuilder builder) {
2. this.value = Arrays.copyOf(builder.getValue(), builder.length());
3. }
1.3 功能函数:
返回数组中的字符
1. public char charAt(int index) {
2. if ((index < 0) || (index >= value.length)) {
3. throw new StringIndexOutOfBoundsException(index);
4. }
5. return value[index];
6. }
//返回这个字符串某个字符的ASCII码值
1. public int codePointAt(int index) {
2. if ((index < 0) || (index >= value.length)) {
3. throw new StringIndexOutOfBoundsException(index);
4. }
5. return Character.codePointAtImpl(value, index, value.length);
6. }
//将字符串指定的内容拷贝到字符数组中指定的位置
1. public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
2. if (srcBegin < 0) {
3. throw new StringIndexOutOfBoundsException(srcBegin);
4. }
5. if (srcEnd > value.length) {
6. throw new StringIndexOutOfBoundsException(srcEnd);
7. }
8. if (srcBegin > srcEnd) {
9. throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
10. }
11. System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
12. }
//获取字符串的字节数组
1. public byte[] getBytes(Charset charset) {
2. if (charset == null) throw new NullPointerException();
3. return StringCoding.encode(charset, value, 0, value.length);//使用stringCoding类中的编码函数进行转换,指定了字符格式
4. }
判断相等函数:重写了equals方法。
1. public boolean equals(Object anObject) {
2. if (this == anObject) { //如果是本身就返回true
3. return true;
4. }
5. if (anObject instanceof String) {
6. String anotherString = (String)anObject;
7. int n = value.length;
8. if (n == anotherString.value.length) {
9. char v1[] = value;
10. char v2[] = anotherString.value;
11. int i = 0;
12. while (n-- != 0) { //进行字符串内容的判断
13. if (v1[i] != v2[i])
14. return false;
15. i++;
16. }
17. return true;
18. }
19. }
20. return false; //如果不是字符串,返回false
21. }
和equals相类似功能的还有contentEquals,也是表示比较内容。equalsIgnoreCase函数是忽略大小写的内容比较。
比较函数:compareTo
1. public int compareTo(String anotherString) {
2. int len1 = value.length;
3. int len2 = anotherString.value.length;
4. int lim = Math.min(len1, len2); //取两者小的长度进行对比
5. char v1[] = value;
6. char v2[] = anotherString.value;
7.
8. int k = 0;
9. while (k < lim) {
10. char c1 = v1[k];
11. char c2 = v2[k];
12. if (c1 != c2) { //如果不相等就返回大小,相等就继续比较
13. return c1 - c2;
14. }
15. k++;
16. }
17. return len1 - len2; //如果前面的字符是一样的,就比较长度
18. }
与比较大小相类似功能的还有一个忽略大小写的比较大小函数:compareToIgnoreCase。这个函数是通过一个静态内部类实现的,主要的比较方法compare函数和上面的基本类似。
区间比较函数:regionMatches ,用于比较两个字符串是不是某一段区间值相同,与之类似的有忽略大小写的区间比较
1. public boolean regionMatches(int toffset, String other, int ooffset,
2. int len) { //开始点,另一个字符串,开始点,比较长度
3. char ta[] = value;
4. int to = toffset;
5. char pa[] = other.value;
6. int po = ooffset;
7. // Note: toffset, ooffset, or len might be near -1>>>1.
8. if ((ooffset < 0) || (toffset < 0)
9. || (toffset > (long)value.length - len)
10. || (ooffset > (long)other.value.length - len)) { //输入校验
11. return false;
12. }
13. while (len-- > 0) {
14. if (ta[to++] != pa[po++]) { //进行对比
15. return false;
16. }
17. }
18. return true;
19. }
是否以某段字符串开头:与这个函数功能类似的有endWith(是否以某个字符串结尾)
1. public boolean startsWith(String prefix, int toffset) {
2. char ta[] = value;
3. int to = toffset;
4. char pa[] = prefix.value;
5. int po = 0;
6. int pc = prefix.value.length;
7. // Note: toffset might be near -1>>>1.
8. if ((toffset < 0) || (toffset > value.length - pc)) { //输入校验
9. return false;
10. }
11. while (--pc >= 0) {
12. if (ta[to++] != pa[po++]) { //进行对比
13. return false;
14. }
15. }
16. return true;
17. }
Hashcode 函数:可以看到,之前object 的equals函数和hashcode函数,string类都进行了重写。下面hash函数的计算公式是 (n是字符长度,c是该位置的ASCII码值)
1. public int hashCode() {
2. int h = hash;
3. if (h == 0 && value.length > 0) {
4. char val[] = value;
5.
6. for (int i = 0; i < value.length; i++) {
7. h = 31 * h + val[i];
8. }
9. hash = h;
10. }
11. return h;
12. }
查找函数:以下函数涉及到较多的比较方法,由于传入的参数是一个整数,所以要进行码值大小的区分,如果是BMP码直接进行进行一位一位比较,如果是增补字符,需要转换高代理单元和低代理单元进行比较,高低代理单元是jdk1.7中utf-16的增补字符,这类字符不代表本身。与之相似的函数:lastIndexOf。
1. public int indexOf(int ch, int fromIndex) {
2. final int max = value.length;
3. if (fromIndex < 0) {
4. fromIndex = 0;
5. } else if (fromIndex >= max) {
6. // Note: fromIndex might be near -1>>>1.
7. return -1;
8. }
9. //前面进行输入校验,下面开始比较,如果是BMP码或者错误的编码
10. if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
11. // handle most cases here (ch is a BMP code point or a
12. // negative value (invalid code point))
13. final char[] value = this.value;
14. for (int i = fromIndex; i < max; i++) {
15. if (value[i] == ch) { //如果字符相等就返回
16. return i;
17. }
18. }
19. return -1;
20. } else {
21. return indexOfSupplementary(ch, fromIndex); //如果是utf-16的增补字符就进行下面比较
22. }
23. }
1. private int indexOfSupplementary(int ch, int fromIndex) {
2. if (Character.isValidCodePoint(ch)) {
3. final char[] value = this.value;
4. final char hi = Character.highSurrogate(ch); //高代理单元
5. final char lo = Character.lowSurrogate(ch); //低代理单元
6. final int max = value.length - 1;
7. for (int i = fromIndex; i < max; i++) {
8. if (value[i] == hi && value[i + 1] == lo) { //校验
9. return i;
10. }
11. }
12. }
13. return -1;
14. }
字符串截取:截取一个字符串从输入值到最后的字符,返回一个新的字符串
1. public String substring(int beginIndex) {
2. if (beginIndex < 0) {
3. throw new StringIndexOutOfBoundsException(beginIndex);
4. }
5. int subLen = value.length - beginIndex;
6. if (subLen < 0) {
7. throw new StringIndexOutOfBoundsException(subLen);
8. }
9. return (beginIndex == 0) ? this : new String(value, beginIndex, subLen); //使用new String 进行
10. }
字符串的拼接:contact是将一个字符串加到另一个后面
1. public String concat(String str) {
2. int otherLen = str.length();
3. if (otherLen == 0) {
4. return this;
5. }
6. int len = value.length;
7. char buf[] = Arrays.copyOf(value, len + otherLen); /使用数组辅助类copy函数进行
8. str.getChars(buf, len); //str值拷贝到buf中
9. return new String(buf, true); //返回新的Str
10. }
//字符串合成,传入分隔符,需要合入的数组字符串
1. public static String join(CharSequence delimiter, CharSequence... elements) {
2. Objects.requireNonNull(delimiter);
3. Objects.requireNonNull(elements);
4. // Number of elements not likely worth Arrays.stream overhead.
5. StringJoiner joiner = new StringJoiner(delimiter); //StringJoiner类是一个专门辅助字符串拼接的类
6. for (CharSequence cs: elements) {
7. joiner.add(cs);
8. }
9. return joiner.toString();
10. }
看下面的一个demo:
System.out.println(String.join("def","1","2"));//输出:1def2
字符串替换:替换函数先去判断是否存在这样的字符,这个能减少创建新的类和字符的优势
1. public String replace(char oldChar, char newChar) {
2. if (oldChar != newChar) {
3. int len = value.length;
4. int i = -1;
5. char[] val = value; /* avoid getfield opcode */
6.
7. while (++i < len) {
8. if (val[i] == oldChar) { //找到第一个字符等于老字符
9. break;
10. }
11. }
12. if (i < len) {
13. char buf[] = new char[len]; //创建一个新的字符
14. for (int j = 0; j < i; j++) { //小于部分直接复制
15. buf[j] = val[j];
16. }
17. while (i < len) { //后面的部分直接替换
18. char c = val[i];
19. buf[i] = (c == oldChar) ? newChar : c;
20. i++;
21. }
22. return new String(buf, true); //返回新的字符串
23. }
24. }
25. return this;
26. }
正则匹配:
1. public String replaceFirst(String regex, String replacement) {
2. return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
3. } //根据正则替换第一个
1. public boolean matches(String regex) {
2. return Pattern.matches(regex, this);
4. } //根据正则匹配
字符分割:根据正则表达式进行匹配分割
1. public String[] split(String regex, int limit) {
2. char ch = 0;
3. if (((regex.value.length == 1 &&
4. ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
5. (regex.length() == 2 &&
6. regex.charAt(0) == '\\' &&
7. (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
8. ((ch-'a')|('z'-ch)) < 0 &&
9. ((ch-'A')|('Z'-ch)) < 0)) &&
10. (ch < Character.MIN_HIGH_SURROGATE ||
11. ch > Character.MAX_LOW_SURROGATE))//如果是以单个字符进行切割
12. {
13. int off = 0;
14. int next = 0;
15. boolean limited = limit > 0;
16. ArrayList<String> list = new ArrayList<>();
17. while ((next = indexOf(ch, off)) != -1) {
18. if (!limited || list.size() < limit - 1) { //不是最后一个
19. list.add(substring(off, next)); //添加到结果中
20. off = next + 1;
21. } else { //最后一个,把剩下的字符当做一个
22. //assert (list.size() == limit - 1);
23. list.add(substring(off, value.length));
24. off = value.length;
25. break;
26. }
27. }
28. // 没匹配上
29. if (off == 0)
30. return new String[]{this};
31.
32. // Add remaining segment
33. if (!limited || list.size() < limit)
34. list.add(substring(off, value.length));
35.
36. // 组装结果
37. int resultSize = list.size();
38. if (limit == 0) {
39. while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
40. resultSize--;
41. }
42. }
43. String[] result = new String[resultSize];
44. return list.subList(0, resultSize).toArray(result);
45. }
46. return Pattern.compile(regex).split(this, limit); //否则进行正则匹配
47. }
转换为小写:还有与之对应的方法是转换为大写,因为要区分不同的区域和语言
1. public String toLowerCase(Locale locale) {
2. if (locale == null) {
3. throw new NullPointerException();
4. }
5.
6. int firstUpper;
7. final int len = value.length;
8.
9. /* 检测字符不是utf-16的新增字符没有字符需要转变 */
10. scan: {
11. for (firstUpper = 0 ; firstUpper < len; ) {
12. char c = value[firstUpper];
13. if ((c >= Character.MIN_HIGH_SURROGATE)
14. && (c <= Character.MAX_HIGH_SURROGATE)) {
15. int supplChar = codePointAt(firstUpper);
16. if (supplChar != Character.toLowerCase(supplChar)) {
17. break scan;
18. }
19. firstUpper += Character.charCount(supplChar);
20. } else {
21. if (c != Character.toLowerCase(c)) {
22. break scan;
23. }
24. firstUpper++;
25. }
26. }
27. return this;
28. }
29.
30. char[] result = new char[len];
31. int resultOffset = 0; /* result may grow, so i+resultOffset
32. * is the write location in result */
33.
34. /* Just copy the first few lowerCase characters. */
35. System.arraycopy(value, 0, result, 0, firstUpper);
36.
37. String lang = locale.getLanguage();
38. boolean localeDependent =
39. (lang == "tr" || lang == "az" || lang == "lt");
40. char[] lowerCharArray;
41. int lowerChar;
42. int srcChar;
43. int srcCount;
44. for (int i = firstUpper; i < len; i += srcCount) {
45. srcChar = (int)value[i];
46. if ((char)srcChar >= Character.MIN_HIGH_SURROGATE
47. && (char)srcChar <= Character.MAX_HIGH_SURROGATE) {
48. srcChar = codePointAt(i);
49. srcCount = Character.charCount(srcChar);
50. } else {
51. srcCount = 1;
52. }
53. if (localeDependent ||
54. srcChar == '\u03A3' || // GREEK CAPITAL LETTER SIGMA
55. srcChar == '\u0130') { // LATIN CAPITAL LETTER I WITH DOT ABOVE
56. lowerChar = ConditionalSpecialCasing.toLowerCaseEx(this, i, locale);
57. } else {
58. lowerChar = Character.toLowerCase(srcChar);
59. }
60. if ((lowerChar == Character.ERROR)
61. || (lowerChar >= Character.MIN_SUPPLEMENTARY_CODE_POINT)) {
62. if (lowerChar == Character.ERROR) {
63. lowerCharArray =
64. ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale);
65. } else if (srcCount == 2) {
66. resultOffset += Character.toChars(lowerChar, result, i + resultOffset) - srcCount;
67. continue;
68. } else {
69. lowerCharArray = Character.toChars(lowerChar);
70. }
71.
72. /* Grow result if needed */
73. int mapLen = lowerCharArray.length;
74. if (mapLen > srcCount) {
75. char[] result2 = new char[result.length + mapLen - srcCount];
76. System.arraycopy(result, 0, result2, 0, i + resultOffset);
77. result = result2;
78. }
79. for (int x = 0; x < mapLen; ++x) {
80. result[i + resultOffset + x] = lowerCharArray[x];
81. }
82. resultOffset += (mapLen - srcCount);
83. } else {
84. result[i + resultOffset] = (char)lowerChar;
85. }
86. }
87. return new String(result, 0, len + resultOffset);
88. }
去掉首尾空格:去掉字符串头尾为空格的函数,这个在读取文件配置的时候,因为空格看不见,所以使用这个函数还是比较有作用的
1. public String trim() {
2. int len = value.length;
3. int st = 0;
4. char[] val = value; /* avoid getfield opcode */
5.
6. while ((st < len) && (val[st] <= ' ')) {
7. st++;
8. }
9. while ((st < len) && (val[len - 1] <= ' ')) {
10. len--;
11. }
12. return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
13. }
转为String 方法:这个是Objetc类的基本方法
1. public String toString() {
2. return this;
3. }
转换为字符数组:将字符串函数转换为字符数组函数
1. public char[] toCharArray() {
2. // Cannot use Arrays.copyOf because of class initialization order issues
3. char result[] = new char[value.length];
4. System.arraycopy(value, 0, result, 0, value.length); //通过系统拷贝返回数组
5. return result;
6. }
格式化:通过格式化方式,将不同的格式化字符转换为相应的字符串
1. public static String format(Locale l, String format, Object... args) {
2. return new Formatter(l).format(format, args).toString();
3. }
将其他类型的转换为String:还有很多基本类型转换为String类的方法。
1. public static String valueOf(Object obj) {
2. return (obj == null) ? "null" : obj.toString(); //返回的toSting方法
3. }
Intern方法:这是个是一个native方法,这个方法和javaStr的常量池有关。调用intern函数,可以将Str类型放入常量池中。
具体看下以下几中Java String的创建来理解。
1. String s = new String("1");
2. s.intern();
3. String s1 = "1";
4. System.out.println(s == s1);//false
5. String s2 = new String("12");
6. s2 = s2.intern();
7. String s3 = "12";
8. System.out.println(s2 == s3);//true
9.
10. String s4 = new String("1") + new String("1");
11. s4.intern();
12. String s5 = "11";
13. System.out.println(s4 == s5);//true
执行完第一个比较返回的是false。第二组是将返回回来的常量池的值直接赋值给s2,所以s2=s3。
下面看下第三个:
第10行执行完之后,内存如图所示:+号是在堆内执行的, 11行执行完之后
以上是关于jdk源码解析--String 类的主要内容,如果未能解决你的问题,请参考以下文章