Java String類型

主要屬性和方法

public final class String
   implments java.io.Serializable, Comparable<String>, CharSequence {
   /** The value is used for character storage. */
   // 用來存儲字符串的值
   private final char value[];

   /** Cache the hash code for the string */
   // 用來緩存hash code 調用hashCode方法時會首先對hash的值進行判斷,若是已經存在值, 因爲String是不可變的,直接返回hash便可,不用從新對該String對象的hash code的進行計算
   // 默認值爲0  
   private int hash; // Default to 0
// 其餘屬性和方法...
}

從源碼String源碼能夠看出,Stringfinal修飾符修飾的類,表示String不能被繼承。String 底層使用final修飾的char[]數組來儲存字符串的值。java

構造方法數組

 

 

String有多種構造構造,能夠傳入char[]StringStringBufferStringBuilder等屬性構造String對象。緩存

因爲String爲不可變類型,當調用String中方法對String修改操做時,都會調用相應的構造方法生成一個新的對象並返回,原String對象並不會產生改變。下面以replace()方法爲例:安全

public String replace(char oldChar, char newChar) {
   // 只有old != new才進行處理 不然直接返回
   if (oldChar != newChar) {
       int len = value.length;
       int i = -1;
       char[] val = value; /* avoid getfield opcode */
       // 經過while循環找到第一個oldChar的位置
       while (++i < len) {
           if (val[i] == oldChar) {
               break;
          }
      }

       if (i < len) {
           // 建立一個新的char數組 用來存新的String對象的值
           char buf[] = new char[len];
           // 將第一個oldChar前的全部char都存入buf數組中
           for (int j = 0; j < i; j++) {
               buf[j] = val[j];
          }
           // 將剩餘val數組中等於oldChar的值修改成newChar後存入buf中
           while (i < len) {
               char c = val[i];
               buf[i] = (c == oldChar) ? newChar : c;
               i++;
          }
           // 根據buf數組生成新的String對象
           return new String(buf, true);
      }
  }
   return this;
}

equals方法併發

//Object中的equals方法
public boolean equals(Object obj) {
   return (this == obj);
}
//String重寫Object的equals方法
public boolean equals(Object anObject) {
   if (this == anObject) {
       return true;
  }
   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;
}

能夠看出Object中的equals方法直接使用「==」判斷兩個對象是否相等,"=="對基本類型進行判斷是,比較的是基本類型的值,而對引用類型的對象進行判斷時,比較的是兩個對象的引用的值是否相等,也就是說"=="判斷兩個對象與對象的內容無關,只與對象的地址有關,當且僅當兩個對象的地址一致時(爲同一個對象)纔會返回true。如:app

String s1 = new String("string");
String s2 = new String("string");
s1 == s2; // false "=="只判斷地址 s1 s2是單獨的兩個對象

String重寫了equals方法,從源碼能夠看出,equals方法傳入的參數類型爲Object,調用equals時,首先會使用"==''比較this和待比較對象anObject的地址是否相等,若相等則表示這個兩個對象爲同一個對象,直接返回true便可。若是不相等,使用instanceof判斷anObject是否爲String類型,若不是則返回false,不然進行下一步操做,先判斷兩個String對象的value數組的長度是否相等,再循環比較數組中的每一個元素是否相等。Stringequals比較的是兩個String對象的內容——也就是value數組是否相等。性能

String s1 = new String("string");
String s2 = new String("string");
s1.equals(s2); // true s1 s2是單獨的兩個對象 可是他們的內容都是 "string"

equals方法外,因爲String實現了comparable接口,也能夠經過comparablecompareTo方法判斷兩個字符串是否相等ui

compareTothis

public int compareTo(String anotherString) {
   int len1 = value.length;
   int len2 = anotherString.value.length;
   int lim = Math.min(len1, len2);
   char v1[] = value;
   char v2[] = anotherString.value;

   int k = 0;
   while (k < lim) {
       char c1 = v1[k];
       char c2 = v2[k];
       if (c1 != c2) {
           return c1 - c2;
      }
       k++;
  }
   return len1 - len2;
}

從源碼能夠看出,compareTo方法的參數類型爲StringequalsObject不一樣,返回值爲intequalsboolean也不同,compareTo方法首先挨個比較兩個字符串中較短字符串的全部元素與較長字符串中對應位置的元素的大小,當對應元素不相等時返回差值,當較短字符串都比較完畢後返回兩個字符串的長度的差值。當返回值爲0時代表這兩個元素相等。返回值<0時,表示按字典順序thisanotherString的前面,反之亦然。atom

因爲String實現Comparable接口,String列表或數組能夠經過Collections.sort或者Arrays.sort方法進行自動排序;同時String對象也能夠做爲有序映射(TreeMap)中的鍵或者有序集合(TreeSet)中的元素,無需指定比較器。

其餘

equalscompareTo都用對應的xxxIgnoreCase方法,equalsIngoreCaseequals相似,先比較地址再比較長度最後經過regionMatches比較兩個字符串忽略小寫以後的內容是否相等

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

compareToIgnoreCase 經過比較器靜態內部類CaseInsensitiveComparatorcompare方法來實現,比較邏輯與compareTo基本相同只是多了一些大小寫轉換後判斷的操做

public int compareToIgnoreCase(String str) {
   return CASE_INSENSITIVE_ORDER.compare(this, str);
}

public static final Comparator<String> CASE_INSENSITIVE_ORDER
   = new CaseInsensitiveComparator();

private static class CaseInsensitiveComparator
   implements Comparator<String>, java.io.Serializable {
   // use serialVersionUID from JDK 1.2.2 for interoperability
   private static final long serialVersionUID = 8575799808933029326L;

   public int compare(String s1, String s2) {
       int n1 = s1.length();
       int n2 = s2.length();
       int min = Math.min(n1, n2);
       for (int i = 0; i < min; i++) {
           char c1 = s1.charAt(i);
           char c2 = s2.charAt(i);
           if (c1 != c2) {
               c1 = Character.toUpperCase(c1);
               c2 = Character.toUpperCase(c2);
               if (c1 != c2) {
                   c1 = Character.toLowerCase(c1);
                   c2 = Character.toLowerCase(c2);
                   if (c1 != c2) {
                       // No overflow because of numeric promotion
                       return c1 - c2;
                  }
              }
          }
      }
       return n1 - n2;
  }
   /** Replaces the de-serialized object. */
   private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
}

其餘方法:

  • indexOf()

  • lastIndexOf()

  • contains()

  • trim()

  • split()

  • ...


String不可變類型

final修飾String類,String類不可繼承。

final修飾char類型的value數組,初始化事後,value指向的數組不能修改,而且每次對對象進行修改時都會經過構造器建立一個新的對象,保證了String的不可變。

設計成不可變的緣由

  1. 安全

  2. 高效

    使用JVM字符串常量池來緩存字符串,只有當字符串爲不可變時,才能實現字符串常量池,因爲String類型使用很頻繁,字符串常量池的存在能有效提升程序的運行效率。


字符串常量池

(JDK1.7以後永久代換成了元空間,將字符串常量池從方法區移到了堆上)

String的常見的建立方式有兩種:

  1. 經過字面量的方式建立 編譯時決定

    String s1 = "string";
    String s2 = "string";
    s1 == s2 // true 字面量建立的String s1 s2都指向字符串常量池中的"string"
  1. 經過new String的方式建立 運行時決定

    String s3 = new String("string");
    String s4 = new String("string");
    s3 == s4 // false s3 s4 指向的是 堆上的內容爲"string"的對象,此時有兩個這種對象

字面量方式建立首先會查找字符串常量池中是否已經存在該字符串,有則直接指向該字符串,不然先在常量池中建立該字符串,而後將引用指向建立的字符串;而經過new建立String,必定會在堆上建立一個字符串對象,而後判斷常量池是否已經存在該字符串的值,若是不存在則會在常量池中建立該字符串,而後將引用的值指向該字符串(s3s4指向堆中對象的地址 而堆中保存字符串常量池中「string「的地址)。

弄清字符串是在編譯時 仍是 運行時 進入常量池

String s1 = "Hello World";
String s2 = "Hello ";
String s3 = "World";
s1 == "Hello " + "World"; //true 字面量相加 直接在編譯期徹底肯定 而且放入字符串常量池中
s1 == s2 + s3; // false 引用相加 不能在編譯期肯定 s2 + s3的值
final String s4 = "Hello ";
final String s5 = "World";
s1 == s4 + s5; // true 在編譯器可以肯定final修飾的s4和s5指向的值

intern

public native String intern();

intern是一個native方法,當使用intern方法時,首先會檢查字符串常量池中,是否已經存在該字符串,若是已存在,直接返回該字符串,不然建立以後再返回。

String s1 = "String";
String s2 = new String("String");
s1 == s2; // false
s1 == s2.intern() // true

StringStringBuffer StringBuilder

因爲String爲不可變類型,每次對String進行修改時都會產生新的String對象,在拼接字符串的時候可能會出現不少無用的String對象,性能會很低,此時就須要StringBuffer來對字符串進行拼接。StringBufferStringBuilder繼承自AbstractStringBuilder,提供了append、insert等方法對字符串進行拼接和修改。

StringBuffer爲線程安全,使用sychronized對方法加鎖實現線程安全,相應的效率也會變低,在非併發的條件下可使用StringBuilder提升效率。

 

 

 

以上爲我的總結的Java String相關知識,若有不對之處,敬請批評指正!

相關文章
相關標籤/搜索