• 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

  • 相关阅读:
    [BZOJ1584][Usaco2009 Mar]Cleaning Up 打扫卫生
    CSS浮动
    Django by example -----1总结
    C#函数重载
    linux目录的特点
    Linux调优
    linux
    对齐方式
    19-10-25-G-悲伤
    19-10-24-H
  • 原文地址:https://www.cnblogs.com/uu5666/p/7779271.html
Copyright © 2020-2023  润新知