Java源码之String

本文出自:http://blog.csdn.net/dt235201314/article/details/78330377

一丶概述

还记得那会的“Hello World”,第一个程序,输出的String,下面介绍String源码,颇有计算机二级考试习题的感觉。

二丶源码及案例

1.String是final类型的

在Java中,被 final 类型修饰的类不允许被其他类继承,被final修饰的变量赋值后不允许被修改。

什么是不可变类?

所谓不可变类,就是创建该类的实例后,该实例的属性是不可改变的,java提供的包装类和java.lang.String类都是不可变类。当创建它们的实例后,其实例的属性是不可改变的。
需要注意的是,对于如下代码

[java] view plain copy
 
  1. String s="abc";  
  2. s="def";  

你可能会感到疑惑,不是说String是不可变类吗,这怎么可以改变呢,平常我也是这样用的啊。请注意,s是字符串对象的”abc”引用,即引用是可以变化的,跟对象实例的属性变化没有什么关系,这点请注意区分。

2.String类实现了Serializable, Comparable, CharSequence接口。

Comparable接口有compareTo(String s)方法,CharSequence接口有length(),charAt(int index),subSequence(int start,int end)方法,后面详解

3.成员变量

[java] view plain copy
 
  1. //用于存储字符串  
  2. private final char value[];  
  3.   
  4. //缓存String的hash值  
  5. private int hash; // Default to 0  

String类中包含一个不可变的char数组用来存放字符串,一个int型的变量hash用来存放计算后的哈希值。


4.构造函数

[java] view plain copy
 
  1. //不含参数的构造函数,一般没什么用,因为value是不可变量  
  2. public String() {  
  3.     this.value = new char[0];  
  4. }  
  5.   
  6. //参数为String类型  
  7. public String(String original) {  
  8.     this.value = original.value;  
  9.     this.hash = original.hash;  
  10. }  
  11.   
  12. //参数为char数组,使用java.utils包中的Arrays类复制  
  13. public String(char value[]) {  
  14.     this.value = Arrays.copyOf(value, value.length);  
  15. }  
  16.   
  17. //从bytes数组中的offset位置开始,将长度为length的字节,以charsetName格式编码,拷贝到value  
  18. public String(byte bytes[], int offset, int length, String charsetName)  
  19.         throws UnsupportedEncodingException {  
  20.     if (charsetName == null)  
  21.         throw new NullPointerException("charsetName");  
  22.     checkBounds(bytes, offset, length);  
  23.     this.value = StringCoding.decode(charsetName, bytes, offset, length);  
  24. }  
  25.   
  26. //调用public String(byte bytes[], int offset, int length, String charsetName)构造函数  
  27. public String(byte bytes[], String charsetName)  
  28.         throws UnsupportedEncodingException {  
  29.     this(bytes, 0, bytes.length, charsetName);  
  30. }  
  31.   
  32. //StringBuffer 和 StringBuider 也可以被当做构造 String 的参数。  
  33. //这两个构造方法是很少用到的,因为当我们有了 StringBuffer 或者 StringBuilfer 对象之后可以直接使用他们的 toString 方法来得到 String。  
  34. public String(StringBuffer buffer) {  
  35.   
  36.     synchronized(buffer) {  
  37.   
  38.     this.value = Arrays.copyOf(buffer.getValue(), buffer.length());  
  39.   
  40.     }  
  41.    
  42. }   
  43.   
  44.   
  45. public String(StringBuilder builder) {  
  46.      this.value = Arrays.copyOf(builder.getValue(), builder.length());  
  47.   
  48. }  


相关问题:

String两种不同的赋值方式

String str = new String("abc");
String str = "abc";

为什么String可以不用new就可以创建对象?这两种赋值方式有什么不同?

例:

[java] view plain copy
 
  1. public class Test {  
  2.   
  3.     public static void main(String[] args) {  
  4.   
  5.     String   a   =   "ok";   // 新建了一个String对象  
  6.     String   b   =   "ok";   // 从缓冲池找  
  7.     String   c   =   new   String("ok");   // 新建一个String对象  
  8.     String   d   =   new   String("ok");   // 不从缓冲池找,新建一个  
  9.       
  10.     System.out.println(a==b);//将输出"true";因为两个变量指向同一个对象。     
  11.     System.out.println(c==d);//将输出"flase";因为两个变量不指向同一个对象。虽然值相同,只有用c.equals(d)才能返回true.  
  12.   
  13.         String e = "a"+"b"+1;  
  14.         String f = "ab1";  
  15.         System.out.println(e == f);//将输出"true";因为编译器识别 “e = "a"+"b"+1”等同于“e = "ab1"”  
  16.   
  17.         String g = new String("ab1");  
  18.         String h = "ab1";  
  19.         System.out.println(g == h);////将输出"flase";因为两个变量不指向同一个对象  
  20.     }  
  21.   
  22. }  

5.常用方法

boolean equals(Object anObject)

[java] view plain copy
 
  1. boolean equals(Object anObject)  
  2.   
  3. public boolean equals(Object anObject) {  
  4.     //如果引用的是同一个对象,返回真  
  5.     if (this == anObject) {  
  6.         return true;  
  7.     }  
  8.     //如果不是String类型的数据,返回假  
  9.     if (anObject instanceof String) {  
  10.         String anotherString = (String) anObject;  
  11.         int n = value.length;  
  12.         //如果char数组长度不相等,返回假  
  13.         if (n == anotherString.value.length) {  
  14.             char v1[] = value;  
  15.             char v2[] = anotherString.value;  
  16.             int i = 0;  
  17.             //从后往前单个字符判断,如果有不相等,返回假  
  18.             while (n-- != 0) {  
  19.                 if (v1[i] != v2[i])  
  20.                         return false;  
  21.                 i++;  
  22.             }  
  23.             //每个字符都相等,返回真  
  24.             return true;  
  25.         }  
  26.     }  
  27.     return false;  
  28. }  


equals方法经常用得到,它用来判断两个对象从实际意义上是否相等,String对象判断规则:
1. 内存地址相同,则为真。
2. 如果对象类型不是String类型,则为假。否则继续判断。
3. 如果对象长度不相等,则为假。否则继续判断。
4. 从后往前,判断String类中char数组value的单个字符是否相等,有不相等则为假。如果一直相等直到第一个数,则返回真。

int compareTo(String anotherString)

[java] view plain copy
 
  1. public int compareTo(String anotherString) {  
  2.     //自身对象字符串长度len1  
  3.     int len1 = value.length;  
  4.     //被比较对象字符串长度len2  
  5.     int len2 = anotherString.value.length;  
  6.     //取两个字符串长度的最小值lim  
  7.     int lim = Math.min(len1, len2);  
  8.     char v1[] = value;  
  9.     char v2[] = anotherString.value;  
  10.   
  11.     int k = 0;  
  12.     //从value的第一个字符开始到最小长度lim处为止,如果字符不相等,返回自身(对象不相等处字符-被比较对象不相等字符)  
  13.     while (k < lim) {  
  14.         char c1 = v1[k];  
  15.         char c2 = v2[k];  
  16.         if (c1 != c2) {  
  17.             return c1 - c2;  
  18.         }  
  19.         k++;  
  20.     }  
  21.     //如果前面都相等,则返回(自身长度-被比较对象长度)  
  22.     return len1 - len2;  
  23. }  

理解:

java中的compareto方法,返回参与比较的前后两个字符串的asc码的差值,看下面一组代码
String a="a",b="b";
System.out.println(a.compareto.b);
则输出-1;
若a="a",b="a"则输出0;
若a="b",b="a"则输出1;
 
单个字符这样比较,若字符串比较长呢??
若a="ab",b="b",则输出-1;
若a="abcdef",b="b"则输出-1;
也就是说,如果两个字符串首字母不同,则该方法返回首字母的asc码的差值;
 
如果首字母相同呢??
若a="ab",b="a",输出1;
若a="abcdef",b="a"输出5;
若a="abcdef",b="abc"输出3;
若a="abcdef",b="ace"输出-1;
即参与比较的两个字符串如果首字符相同,则比较下一个字符,直到有不同的为止,返回该不同的字符的asc码差值,如果两个字符串不一样长,可以参与比较的字符又完全一样,则返回两个字符串的长度差值

int hashCode()

[java] view plain copy
 
  1. int hashCode()  
  2.   
  3. public int hashCode() {  
  4.     int h = hash;  
  5.     //如果hash没有被计算过,并且字符串不为空,则进行hashCode计算  
  6.     if (h == 0 && value.length > 0) {  
  7.         char val[] = value;  
  8.   
  9.         //计算过程  
  10.         //s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]  
  11.         for (int i = 0; i < value.length; i++) {  
  12.             h = 31 * h + val[i];  
  13.         }  
  14.         //hash赋值  
  15.         hash = h;  
  16.     }  
  17.     return h;  
  18. }  

String类重写了hashCode方法,Object中的hashCode方法是一个Native调用。String类的hash采用多项式计算得来,我们完全可以通过不相同的字符串得出同样的hash,所以两个String对象的hashCode相同,并不代表两个String是一样的。

boolean startsWith(String prefix,int toffset)

[java] view plain copy
 
  1. boolean startsWith(String prefix,int toffset)  
  2.   
  3. public boolean startsWith(String prefix, int toffset) {  
  4.     char ta[] = value;  
  5.     int to = toffset;  
  6.     char pa[] = prefix.value;  
  7.     int po = 0;  
  8.     int pc = prefix.value.length;  
  9.     // Note: toffset might be near -1>>>1.  
  10.     //如果起始地址小于0或者(起始地址+所比较对象长度)大于自身对象长度,返回假  
  11.     if ((toffset < 0) || (toffset > value.length - pc)) {  
  12.         return false;  
  13.     }  
  14.     //从所比较对象的末尾开始比较  
  15.     while (--pc >= 0) {  
  16.         if (ta[to++] != pa[po++]) {  
  17.             return false;  
  18.         }  
  19.     }  
  20.     return true;  
  21. }  
  22.   
  23. public boolean startsWith(String prefix) {  
  24.     return startsWith(prefix, 0);  
  25. }  
  26.   
  27. public boolean endsWith(String suffix) {  
  28.     return startsWith(suffix, value.length - suffix.value.length);  
  29. }  

起始比较和末尾比较都是比较经常用得到的方法,例如在判断一个字符串是不是http协议的,或者初步判断一个文件是不是mp3文件,都可以采用这个方法进行比较

String concat(String str)

[java] view plain copy
 
  1. public String concat(String str) {  
  2.     int otherLen = str.length();  
  3.     //如果被添加的字符串为空,返回对象本身  
  4.     if (otherLen == 0) {  
  5.         return this;  
  6.     }  
  7.     int len = value.length;  
  8.     char buf[] = Arrays.copyOf(value, len + otherLen);  
  9.     str.getChars(buf, len);  
  10.     return new String(buf, true);  
  11. }  

concat方法也是经常用的方法之一,它先判断被添加字符串是否为空来决定要不要创建新的对象。


String replace(char oldChar,char newChar)

[java] view plain copy
 
  1. public String replace(char oldChar, char newChar) {  
  2.     //新旧值先对比  
  3.     if (oldChar != newChar) {  
  4.         int len = value.length;  
  5.         int i = -1;  
  6.         char[] val = value; /* avoid getfield opcode */  
  7.   
  8.         //找到旧值最开始出现的位置  
  9.         while (++i < len) {  
  10.             if (val[i] == oldChar) {  
  11.                 break;  
  12.             }  
  13.         }  
  14.         //从那个位置开始,直到末尾,用新值代替出现的旧值  
  15.         if (i < len) {  
  16.             char buf[] = new char[len];  
  17.             for (int j = 0; j < i; j++) {  
  18.                 buf[j] = val[j];  
  19.             }  
  20.             while (i < len) {  
  21.                 char c = val[i];  
  22.                 buf[i] = (c == oldChar) ? newChar : c;  
  23.                 i++;  
  24.             }  
  25.             return new String(buf, true);  
  26.         }  
  27.     }  
  28.     return this;  
  29. }  

这个方法也有讨巧的地方,例如最开始先找出旧值出现的位置,这样节省了一部分对比的时间。replace(String oldStr,String newStr)方法通过正则表达式来判断。

String trim()

[java] view plain copy
 
  1. public String trim() {  
  2.     int len = value.length;  
  3.     int st = 0;  
  4.     char[] val = value;    /* avoid getfield opcode */  
  5.   
  6.     //找到字符串前段没有空格的位置   
  7.     while ((st < len) && (val[st] <= ' ')) {  
  8.         st++;  
  9.     }  
  10.     //找到字符串末尾没有空格的位置  
  11.     while ((st < len) && (val[len - 1] <= ' ')) {  
  12.         len--;  
  13.     }  
  14.     //如果前后都没有出现空格,返回字符串本身,左右有空格则去掉空格  
  15.     return ((st > 0) || (len < value.length)) ? substring(st, len) : this;  
  16. }  

ASCII值比较,“”为32,字母都大于64,可参考ASCII值表

String substring()

[java] view plain copy
 
  1. // 截取字符串  
  2.  public String substring(int beginIndex) {  
  3.      if (beginIndex < 0) { // 如果起始下标<0  
  4.          throw new StringIndexOutOfBoundsException(beginIndex);  
  5.      }  
  6.      int subLen = value.length - beginIndex; // 获取截取长度  
  7.      if (subLen < 0) { // 如果截取长度<0  
  8.          throw new StringIndexOutOfBoundsException(subLen);  
  9.      }  
  10.      return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);  
  11.  }  
[java] view plain copy
 
  1. // 截取字符串  
  2.  public String substring(int beginIndex, int endIndex) {  
  3.      if (beginIndex < 0) { // 如果起始下标<0  
  4.          throw new StringIndexOutOfBoundsException(beginIndex);  
  5.      }  
  6.      if (endIndex > value.length) { // 如果末尾下标>字符数组长度  
  7.          throw new StringIndexOutOfBoundsException(endIndex);  
  8.      }  
  9.      int subLen = endIndex - beginIndex; // 获取截取长度  
  10.      if (subLen < 0) { // 如果截取长度<0  
  11.          throw new StringIndexOutOfBoundsException(subLen);  
  12.      }  
  13.      return ((beginIndex == 0) && (endIndex == value.length)) ? this  
  14.              : new String(value, beginIndex, subLen);  
  15.  }  

如:

"hamburger".substring(4) returns "urger"
"hamburger".substring(4, 8) returns "urge"
 "smiles".substring(1, 5) returns "mile"

indexOf():

[java] view plain copy
 
  1. //该字符跟数组中的每个字符从左往右比较    
  2. //lastIndexOf一样只不过是从右往左比较    
  3. public int indexOf(int ch, int fromIndex) {    
  4.         final int max = value.length;    
  5.         if (fromIndex < 0) {    
  6.             fromIndex = 0;    
  7.         } else if (fromIndex >= max) {    
  8.             // Note: fromIndex might be near -1>>>1.    
  9.             return -1;    
  10.         }    
  11.     
  12.         if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {    
  13.             // handle most cases here (ch is a BMP code point or a    
  14.             // negative value (invalid code point))    
  15.             final char[] value = this.value;    
  16.             for (int i = fromIndex; i < max; i++) {    
  17.                 if (value[i] == ch) {    
  18.                     return i;    
  19.                 }    
  20.             }    
  21.             return -1;    
  22.         } else {    
  23.             return indexOfSupplementary(ch, fromIndex);    
  24.         }    
  25. }    
  26. //indexOf最终是调用下面的第二个static方法来进行求解的    
  27. //求解步骤大概是:    
  28. //首先搜索到第一个字符所在的位置,之后逐个比较;    
  29. //这里并没有使用kmp算法因此是一个可以优化的地方    
  30. public int indexOf(String str, int fromIndex) {    
  31.         return indexOf(value, 0, value.length,    
  32.                 str.value, 0, str.value.length, fromIndex);    
  33. }    
  34. static int indexOf(char[] source, int sourceOffset, int sourceCount,    
  35.             char[] target, int targetOffset, int targetCount,    
  36.             int fromIndex) {    
  37.         if (fromIndex >= sourceCount) {    
  38.             return (targetCount == 0 ? sourceCount : -1);    
  39.         }    
  40.         if (fromIndex < 0) {    
  41.             fromIndex = 0;    
  42.         }    
  43.         if (targetCount == 0) {    
  44.             return fromIndex;    
  45.         }    
  46.     
  47.         char first = target[targetOffset];    
  48.         int max = sourceOffset + (sourceCount - targetCount);    
  49.     
  50.         for (int i = sourceOffset + fromIndex; i <= max; i++) {    
  51.             /* Look for first character. */    
  52.             if (source[i] != first) {    
  53.                 while (++i <= max && source[i] != first);    
  54.             }    
  55.     
  56.             /* Found first character, now look at the rest of v2 */    
  57.             if (i <= max) {    
  58.                 int j = i + 1;    
  59.                 int end = j + targetCount - 1;    
  60.                 for (int k = targetOffset + 1; j < end && source[j]    
  61.                         == target[k]; j++, k++);    
  62.     
  63.                 if (j == end) {    
  64.                     /* Found whole string. */    
  65.                     return i - sourceOffset;    
  66.                 }    
  67.             }    
  68.         }    
  69.         return -1;    
  70. }    

split

[java] view plain copy
 
  1. public String[] split(String regex, int limit) {    
  2.   
  3.         char ch = 0;  
  4. //1. 如果regex只有一位,且不为列出的特殊字符;  
  5. //2.如regex有两位,第一位为转义字符且第二位不是数字或字母,“|”表示或,即只要ch小于0或者大于9任一成立,小于a或者大于z任一成立,小于A或大于Z任一成立  
  6. //3.第三个是和编码有关,就是不属于utf-16之间的字符    
  7.         if (((regex.value.length == 1 &&    
  8.              ".$|()[{^?*+\".indexOf(ch = regex.charAt(0)) == -1) ||    
  9.              (regex.length() == 2 &&    
  10.               regex.charAt(0) == '\' &&    
  11.               (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&    
  12.               ((ch-'a')|('z'-ch)) < 0 &&    
  13.               ((ch-'A')|('Z'-ch)) < 0)) &&    
  14.             (ch < Character.MIN_HIGH_SURROGATE ||    
  15.              ch > Character.MAX_LOW_SURROGATE))    
  16.         {    
  17.             int off = 0;    
  18.             int next = 0;    
  19.             boolean limited = limit > 0;    
  20.             ArrayList<String> list = new ArrayList<>();  
  21. //如果这个字符串中包含了用于分割的ch,则进行下一步操作    
  22.             while ((next = indexOf(ch, off)) != -1) {    
  23.                 if (!limited || list.size() < limit - 1) {    
  24.                     list.add(substring(off, next));    
  25.                     off = next + 1;    
  26.                 } else {      
  27.     
  28.                     list.add(substring(off, value.length));    
  29.                     off = value.length;    
  30.                     break;    
  31.                 }    
  32.             }    
  33.             // If no match was found, return this    
  34.             if (off == 0)    
  35.                 return new String[]{this};    
  36.     
  37.             // Add remaining segment    
  38.             if (!limited || list.size() < limit)    
  39.                 list.add(substring(off, value.length));    
  40.     
  41.             // Construct result    
  42.             int resultSize = list.size();    
  43.             if (limit == 0)    
  44.                 while (resultSize > 0 && list.get(resultSize - 1).length() == 0)    
  45.                     resultSize--;    
  46.             String[] result = new String[resultSize];    
  47.             return list.subList(0, resultSize).toArray(result);    
  48.         }    
  49.         return Pattern.compile(regex).split(this, limit);    
  50.     }    

详解见:

String.split()方法你可能不知道的一面

String intern()

[java] view plain copy
 
  1. public native String intern();  

intern方法是Native调用,它的作用是在方法区中的常量池里通过equals方法寻找等值的对象,如果没有找到则在常量池中开辟一片空间存放字符串并返回该对应String的引用,否则直接返回常量池中已存在String对象的引用

例:

[java] view plain copy
 
  1. String a = new String("ab1");  
  2. String b = new String("ab1").intern();  

a == b就为true

 

int hash32()

[java] view plain copy
 
  1. int hash32()  
  2.   
  3. private transient int hash32 = 0;  
  4. int hash32() {  
  5.     int h = hash32;  
  6.     if (0 == h) {  
  7.        // harmless data race on hash32 here.  
  8.        h = sun.misc.Hashing.murmur3_32(HASHING_SEED, value, 0, value.length);  
  9.   
  10.        // ensure result is not zero to avoid recalcing  
  11.        h = (0 != h) ? h : 1;  
  12.   
  13.        hash32 = h;  
  14.     }  
  15.   
  16.     return h;  
  17. }  

在JDK1.7中,Hash相关集合类在String类作key的情况下,不再使用hashCode方式离散数据,而是采用hash32方法。这个方法默认使用系统当前时间,String类地址,System类地址等作为因子计算得到hash种子,通过hash种子在经过hash得到32位的int型数值。

其他方法:

[java] view plain copy
 
  1. public int length() {  
  2.     return value.length;  
  3. }  
  4. public String toString() {  
  5.     return this;  
  6. }  
  7. public boolean isEmpty() {  
  8.     return value.length == 0;  
  9. }  
  10. public char charAt(int index) {  
  11.     if ((index < 0) || (index >= value.length)) {  
  12.         throw new StringIndexOutOfBoundsException(index);  
  13.     }  
  14.     return value[index];  
  15. }  

三丶参考文章
String 源码解析,深入认识String

原文地址:https://www.cnblogs.com/uu5666/p/7779271.html