String(JDK1.8)原始碼閱讀記錄

pzwdshxzt發表於2018-09-06
版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/weixin_40254498/article/details/82464207

String

  • 在 Java 中字串屬於物件。
  • Java 提供了 String 類來建立和操作字串。

定義

使用了final ,說明該類不能被繼承。同時還實現了:

  • java.io.Serializable
  • Comparable
  • CharSequence
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence { }

屬性


 /** The value is used for character storage. 
  * String就是用char[]實現的。儲存的  
  */
 private final char value[];

 /** Cache the hash code for the string 
 * hash 值
 */
 private int hash; // Default to 0

 /** use serialVersionUID from JDK 1.0.2 for interoperability 
 * Java的序列化機制是通過在執行時判斷類的serialVersionUID來驗證版本一致性的。
 */
 private static final long serialVersionUID = -6849794470754667710L;

 /**
  * Class String is special cased within the Serialization Stream Protocol.
  * 類字串在序列化流協議中是特殊的。
  * A String instance is written into an ObjectOutputStream according to
  * 將字串例項寫入ObjectOutputStream中,根據 a標籤
  * <a href="{@docRoot}/../platform/serialization/spec/output.html">
  * Object Serialization Specification, Section 6.2, "Stream Elements"</a>
  */
 private static final ObjectStreamField[] serialPersistentFields =
     new ObjectStreamField[0];

構造方法

String 的構造方法大概有十幾種,其中最常用的如下:

/**
 * 根據字串建立字串物件
 * Initializes a newly created {@code String} object so that it represents
 * the same sequence of characters as the argument; in other words, the
 * newly created string is a copy of the argument string. Unless an
 * explicit copy of {@code original} is needed, use of this constructor is
 * unnecessary since Strings are immutable.
 *
 * @param  original
 *         A {@code String}
 */
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

/**
 * 根據byte陣列建立字串物件
 * byte[] to String 是根據系統的編碼來的,但是也可以自己指定編碼
 * Constructs a new {@code String} by decoding the specified array of bytes
 * using the platform`s default charset.  The length of the new {@code
 * String} is a function of the charset, and hence may not be equal to the
 * length of the byte array.
 *
 * <p> The behavior of this constructor when the given bytes are not valid
 * in the default charset is unspecified.  The {@link
 * java.nio.charset.CharsetDecoder} class should be used when more control
 * over the decoding process is required.
 *
 * @param  bytes The bytes to be decoded into characters
 * @since  JDK1.1
 */
public String(byte bytes[]) {
    this(bytes, 0, bytes.length);
}

/**
 * 在Java中,String例項中儲存有一個char[]字元陣列,char[]字元陣列是以unicode碼來儲存的,
 * String 和 char 為記憶體形式,byte是網路傳輸或儲存的序列化形式。 
 * 所以在很多傳輸和儲存的過程中需要將byte[]陣列和String進行相互轉化。 
 * 所以,String提供了一系列過載的構造方法來將一個字元陣列轉化成String, 
 * 提到byte[]和String之間的相互轉換就不得不關注編碼問題。  
 * 例如:
 * public String(byte bytes[], int offset, int length, Charset charset) {} 
 * String(byte bytes[], String charsetName)
 * String(byte bytes[], int offset, int length, String charsetName) 
 * and so on
 * String(byte[] bytes, Charset charset)是指通過charset來解碼指定的byte陣列, 
 * 將其解碼成unicode的char[]陣列,夠造成新的String。 
 * 
 * 下面這個構造方法可以指定位元組陣列的編碼  
 * Constructs a new {@code String} by decoding the specified array of
 * bytes using the specified {@linkplain java.nio.charset.Charset charset}.
 * The length of the new {@code String} is a function of the charset, and
 * hence may not be equal to the length of the byte array.
 *
 * <p> This method always replaces malformed-input and unmappable-character
 * sequences with this charset`s default replacement string.  The {@link
 * java.nio.charset.CharsetDecoder} class should be used when more control
 * over the decoding process is required.
 *
 * @param  bytes
 *         The bytes to be decoded into characters
 *
 * @param  charset
 *         The {@linkplain java.nio.charset.Charset charset} to be used to
 *         decode the {@code bytes}
 *
 * @since  1.6
 */
 public String(byte bytes[], Charset charset) {
     this(bytes, 0, bytes.length, charset);
 }

/**
 * 根據char陣列
 * Allocates a new {@code String} so that it represents the sequence of
 * characters currently contained in the character array argument. The
 * contents of the character array are copied; subsequent modification of
 * the character array does not affect the newly created string.
 *
 * @param  value
 *         The initial value of the string
 */
public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);

/**
 * 根據 StringBuffer 建立 String物件
 * Allocates a new string that contains the sequence of characters
 * currently contained in the string buffer argument. The contents of the
 * string buffer are copied; subsequent modification of the string buffer
 * does not affect the newly created string.
 *
 * @param  buffer
 *         A {@code StringBuffer}
 */
public String(StringBuffer buffer) {
    synchronized(buffer) {
        this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
    }
}

/**
 * 根據 StringBuilder 建立 String物件
 * Allocates a new string that contains the sequence of characters
 * currently contained in the string builder argument. The contents of the
 * string builder are copied; subsequent modification of the string builder
 * does not affect the newly created string.
 *
 * <p> This constructor is provided to ease migration to {@code
 * StringBuilder}. Obtaining a string from a string builder via the {@code
 * toString} method is likely to run faster and is generally preferred.
 *
 * @param   builder
 *          A {@code StringBuilder}
 *
 * @since  1.5
 */
public String(StringBuilder builder) {
    this.value = Arrays.copyOf(builder.getValue(), builder.length());
}

 /*
  * 這是一個受保護構造方法,因為不能繼承,所以內部使用
  * 第二個屬性基本沒有用,只能是true
  * 從程式碼中可以看出來是直接引用,而不是新建一個,為了提高效能,節省記憶體等。
  * 保護的原因也是為了保證字串不可修改。
  * Package private constructor which shares value array for speed.
  * this constructor is always expected to be called with share==true.
  * a separate constructor is needed because we already have a public
  * String(char[]) constructor that makes a copy of the given char[].
  */
  String(char[] value, boolean share) {
      // assert share : "unshared not supported";
      this.value = value;
  }

常用的方法

getByte

/**
 * 將字串轉成可用的 byte陣列
 * 在通訊的比較多,例如  網路中傳輸、8583報文、socket通訊 
 * 要想不亂碼,就得搞清楚通訊雙方所使用的位元組編碼!!!
 * Encodes this {@code String} into a sequence of bytes using the named
 * charset, storing the result into a new byte array.
 *
 * <p> The behavior of this method when this string cannot be encoded in
 * the given charset is unspecified.  The {@link
 * java.nio.charset.CharsetEncoder} class should be used when more control
 * over the encoding process is required.
 *
 * @param  charsetName
 *         The name of a supported {@linkplain java.nio.charset.Charset
 *         charset}
 *
 * @return  The resultant byte array
 *
 * @throws  UnsupportedEncodingException
 *          If the named charset is not supported
 *
 * @since  JDK1.1
 */
public byte[] getBytes(String charsetName)
        throws UnsupportedEncodingException {
    if (charsetName == null) throw new NullPointerException();
    return StringCoding.encode(charsetName, value, 0, value.length);
}

/**
 * 同上
 * Encodes this {@code String} into a sequence of bytes using the given
 * {@linkplain java.nio.charset.Charset charset}, storing the result into a
 * new byte array.
 *
 * <p> This method always replaces malformed-input and unmappable-character
 * sequences with this charset`s default replacement byte array.  The
 * {@link java.nio.charset.CharsetEncoder} class should be used when more
 * control over the encoding process is required.
 *
 * @param  charset
 *         The {@linkplain java.nio.charset.Charset} to be used to encode
 *         the {@code String}
 *
 * @return  The resultant byte array
 *
 * @since  1.6
 */
public byte[] getBytes(Charset charset) {
    if (charset == null) throw new NullPointerException();
    return StringCoding.encode(charset, value, 0, value.length);
}
/**
 * 將使用系統預設編碼。
 * 要注意的,部署的時候容易出錯的地方就是這裡,
 * windows 環境和linux環境位元組編碼不一樣.所以建議指定編碼方法
 * Encodes this {@code String} into a sequence of bytes using the
 * platform`s default charset, storing the result into a new byte array.
 *
 * <p> The behavior of this method when this string cannot be encoded in
 * the default charset is unspecified.  The {@link
 * java.nio.charset.CharsetEncoder} class should be used when more control
 * over the encoding process is required.
 *
 * @return  The resultant byte array
 *
 * @since      JDK1.1
 */
public byte[] getBytes() {
    return StringCoding.encode(value, 0, value.length);
}

hashCode

/**
 * hash演算法
 * hashCode可以保證相同的字串的hash值肯定相同,
 * 但是,hash值相同並不一定是value值就相同。 
 * 所以要保證兩個字串相等還得用上 equals
 * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
 */
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

equals

/**
  * 
  * 在hashmap中
  * 一定要重寫 equals 和 hachcode 
  * 才能保證是同一個字串
  * 正因為String 重寫了我們才能愉快的使用字串作為key
  */
 public boolean equals(Object anObject) {
     /** 首先判斷是不是自己!*/
     if (this == anObject) {
         return true;
     }
     /** 在判斷是不是String型別 */
     if (anObject instanceof String) {
         String anotherString = (String)anObject;
         int n = value.length;
         /** 判斷長度 */
         if (n == anotherString.value.length) {
             char v1[] = value;
             char v2[] = anotherString.value;
             int i = 0;
             /** 判斷位元組 */
             while (n-- != 0) {
                 if (v1[i] != v2[i])
                     return false;
                 i++;
             }
             return true;
         }
     }
     return false;
 }

substring

這個方法在JDK1.6(含1.6)以前和JDK1.7之後(含1.7)有了不一樣的變化

JDK1.6 substring

/** 
 * 仍然建立新的字串但是 舊字串還在 只是新的引用了舊的一部分
 * 但舊字串很大的時候,因為新的引用一小部分而無法回收會導致記憶體洩漏
 * 一般使用加上一個空的字串來生成新的解決這個問題
 * str = str.substring(x, y) + ""
 */
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

public String substring(int beginIndex, int endIndex) {
    /** 校驗陣列溢位 */
    return  new String(offset + beginIndex, endIndex - beginIndex, value);
}
  • 記憶體洩露:在電腦科學中,記憶體洩漏指由於疏忽或錯誤造成程式未能釋放已經不再使用的記憶體。 記憶體洩漏並非指記憶體在物理上的消失,而是應用程式分配某段記憶體後,由於設計錯誤,導致在釋放該段記憶體之前就失去了對該段記憶體的控制,從而造成了記憶體的浪費。

JDK1.8 substring

jdk1.7之後直接新建了一個字串 。雖然增加了記憶體,但是解決了記憶體洩漏問題。

public String substring(int beginIndex, int endIndex) {

    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    if (endIndex > value.length) {
        throw new StringIndexOutOfBoundsException(endIndex);
    }
    int subLen = endIndex - beginIndex;
    if (subLen < 0) {
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return ((beginIndex == 0) && (endIndex == value.length)) ? this
            : new String(value, beginIndex, subLen);

public String(char value[], int offset, int count) {
   if (offset < 0) {
        throw new StringIndexOutOfBoundsException(offset);
    }
    if (count <= 0) {
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        if (offset <= value.length) {
            this.value = "".value;
            return;
        }
    }
    // Note: offset or count might be near -1>>>1.
    if (offset > value.length - count) {
        throw new StringIndexOutOfBoundsException(offset + count);
    }
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}

valueOf

/** 呼叫物件自己的toString方法 */
public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}
public static String valueOf(char data[]) {
    return new String(data);
}
public static String valueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}

String + 號過載

 String str = "abc";
 String str1= str + "def";

 /** 反編譯之後 */
 String str = "abc";
 String str1= (new StringBuilder(String.valueOf(str))).append("def").toString();

spilt

按照字元regex將字串分成limit份。

 public String[] split(String regex, int limit) {
     /* fastpath if the regex is a
      (1)one-char String and this character is not one of the
         RegEx`s meta characters ".$|()[{^?*+\", or
      (2)two-char String and the first char is the backslash and
         the second is not the ascii digit or ascii letter.
      */
     char ch = 0;
     if (((regex.value.length == 1 &&
          ".$|()[{^?*+\".indexOf(ch = regex.charAt(0)) == -1) ||
          (regex.length() == 2 &&
           regex.charAt(0) == `\` &&
           (((ch = regex.charAt(1))-`0`)|(`9`-ch)) < 0 &&
           ((ch-`a`)|(`z`-ch)) < 0 &&
           ((ch-`A`)|(`Z`-ch)) < 0)) &&
         (ch < Character.MIN_HIGH_SURROGATE ||
          ch > Character.MAX_LOW_SURROGATE))
     {
         int off = 0;
         int next = 0;
         boolean limited = limit > 0;
         ArrayList<String> list = new ArrayList<>();
         while ((next = indexOf(ch, off)) != -1) {
             if (!limited || list.size() < limit - 1) {
                 list.add(substring(off, next));
                 off = next + 1;
             } else {    // last one
                 //assert (list.size() == limit - 1);
                 list.add(substring(off, value.length));
                 off = value.length;
                 break;
             }
         }
         // If no match was found, return this
         if (off == 0)
             return new String[]{this};

         // Add remaining segment
         if (!limited || list.size() < limit)
             list.add(substring(off, value.length));

         // Construct result
         int resultSize = list.size();
         if (limit == 0) {
             while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                 resultSize--;
             }
         }
         String[] result = new String[resultSize];
         return list.subList(0, resultSize).toArray(result);
     }
     return Pattern.compile(regex).split(this, limit);
 }

按照字元regex將字串分割

 /** 直接呼叫 split(String regex, int limit) limit 為 零 */
 public String[] split(String regex) {
    return split(regex, 0);
  }

equalsIgnoreCase

public boolean equalsIgnoreCase(String anotherString) {
        return (this == anotherString) ? true
                : (anotherString != null)
                && (anotherString.value.length == value.length)
                && regionMatches(true, 0, anotherString, 0, value.length);
    }

三目運算子加 && 代替 多個if

replaceFirst、replaceAll、replace

String replaceFirst(String regex, String replacement)
String replaceAll(String regex, String replacement)
String replace(CharSequence target, CharSequence replacement)
  • replace的引數是char和CharSequence,即可以支援字元的替換,也支援字串的替換
  • replaceAll和replaceFirst的引數是regex,即基於規則表示式的替換,replace只要有符合就替換
  • replaceFirst(),只替換第一次出現的字串;

其他方法

String 類中還有很多方法。例如:

  • public int length(){}
    返回字串長度
  • public boolean isEmpty() { }
    返回字串是否為空
  • public char charAt(int index) {}
    返回字串中第(index+1)個字元
  • public char[] toCharArray() {}
    轉化成字元陣列
  • public String trim(){}
    去掉兩端空格
  • public String toUpperCase(){}
    轉化為大寫
  • public String toLowerCase(){}
    轉化為小寫
  • public String concat(String str) {}
    拼接字串
  • public boolean matches(String regex){}
    判斷字串是否匹配給定的regex正規表示式
  • public boolean contains(CharSequence s)
    判斷字串是否包含字元序列s


相關文章