jdk源码解析--String 类

Posted 我的IT技术路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jdk源码解析--String 类相关的知识,希望对你有一定的参考价值。

Java语言中,字符串有着较多的函数,功能实现也比较齐全。看下jdk1.8String类的具体实现。

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 == nullthrow 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.7utf-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 类的主要内容,如果未能解决你的问题,请参考以下文章

jdk源码解析--AbstractStringBuilder类

JDK源码及其他框架源码解析随笔地址导航

jdk源码解析--PrintStream类

jdk源码解析--LongAdder类

jdk源码解析--BitSet类

jdk源码解析--WeakReference/softReference类