Java 基礎:String 類源碼分析

String 類實現接口屬性一、私有屬性私有類 CaseInsensitiveComparatorString 構造方法一、無參構造函數二、參數爲 String 類型三、參數爲字符數組四、參數爲 int 數組五、參數爲字節數組六、參數爲 StringBuilder 或 StringBuffer七、特殊的 protected 構造方法其餘方法一、concat 鏈接函數二、getBytes三、equals 和 hashCode四、比較方法五、先後綴斷定六、索引獲取七、字符串截取八、字符串替換九、valueOf十、intern()方法參考連接 java

Java 中數據類型分爲兩大類:基礎數據類型(byte,short,int,long,float,double,char,boolean)和引用類型(String 類型和基礎數據類型的包裝類),能夠看出 String 類型是很是特殊的,同時也是編寫代碼過程當中使用比較頻繁的一種類型,爲了更好的瞭解該類型,決心鑽研一下 String 類源碼,但願能有所收穫。web

String 類實現接口

public final class String implements SerializableComparable<String>, CharSequence
複製代碼

從該類的聲明中咱們能夠看出String是final類型的,表示該類不能被繼承,同時該類實現了三個接口。面試

  • Serializable 接口是爲了實現類對象的序列化,主要是能把堆內存中的對象的生命週期延長,作持久化操做。當下次再須要這個對象的時候,咱們不用 new了,直接從硬盤中讀取就能夠了。
  • Comparable 接口強行對實現它的每一個類的對象進行總體排序。此排序被稱爲該類的天然排序 ,類的 compareTo 方法被稱爲它的天然比較方法 。實現此接口的對象列表(和數組)能夠經過 Collections.sort(和 Arrays.sort)進行自動排序。實現此接口的對象能夠用做有序映射表中的鍵或有序集合中的元素,無需指定比較器。後續會講解 String 類中的 compareTo 方法。
  • CharSequence 是一個接口,它只包括 length(), charAt(int index), subSequence(int start, int end)這幾個 API 接口。除了 String 實現了 CharSequence 以外,StringBuffer 和 StringBuilder 也實現了 CharSequence 接口。CharSequence 就是字符序列,String, StringBuilder 和 StringBuffer 本質上都是經過字符數組實現的!

屬性

一、私有屬性

String 的底層是由 char 數組構成的正則表達式

private final char[] value;
private int hash;
複製代碼

因爲底層 char 數組是 final 的,因此 String 對象是不可變的,且不可被繼承。 value:是一個 private
final 修飾的 char 數組,String 類是經過該數組來存在字符串的。 hash:是一個 private 修飾的 int
變量,用來存放 String 對象的 hashCode。數據庫

private staticfinal long serialVersionUID = -6849794470754667710L;
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
複製代碼

由於String實現了Serializable接口,因此支持序列化和反序列化支持。Java的序列化機制是經過在運行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,若是相同就認爲是一致的,能夠進行反序列化,不然就會出現序列化版本不一致的異常(InvalidCastException)。編程

私有類 CaseInsensitiveComparator

private static class CaseInsensitiveComparator implements Comparator<String>, Serializable
複製代碼

該類一樣實現了 Comparator 和 Serializable 接口,用於 String 類對象的排序。segmentfault

public int compare(String var1, String var2) {
    int var3 = var1.length();
    int var4 = var2.length();
    int var5 = Math.min(var3, var4);

    for(int var6 = 0; var6 < var5; ++var6) {
        char var7 = var1.charAt(var6);
        char var8 = var2.charAt(var6);
        if (var7 != var8) {
            var7 = Character.toUpperCase(var7);
            var8 = Character.toUpperCase(var8);
            if (var7 != var8) {
                var7 = Character.toLowerCase(var7);
                var8 = Character.toLowerCase(var8);
                if (var7 != var8) {
                    return var7 - var8;
                }
            }
        }
    }

    return var3 - var4;
}
複製代碼

從源碼來看,在該類中存在一個 compare 方法,方法對兩個對象的比較流程以下:循環的次數爲長度最小的字符串的長度;從頭開始比較每一個字符,若是不相等則轉換爲大寫再比較,再不相等轉換爲小寫比較,最後字符之間相減。減法操做會將獲取字符的 hashCode,而後相減(漢字也是如此)。若是循環過程當中字符比較都相等,最後返回兩個字符串對象長度的差值。數組

String 構造方法

一、無參構造函數

public String() {
    this.value = "".value;
}
複製代碼
String s = new String();
System.out.println(s);  //值爲"",也就是空字符串
System.out.println(s.hashCode());   //hash未賦初始值,因此默認值爲0,後期再詳細講hashCode方法
複製代碼

二、參數爲 String 類型

public String(String var1) {
    this.value = var1.value;
    this.hash = var1.hash;
}
複製代碼

首先聲明一下 Java 的語法是容許在一個類中訪問該類的實例對象的私有屬性的。可是其餘類就不可了。緩存

三、參數爲字符數組

public String(char[] var1) {
    this.value = Arrays.copyOf(var1, var1.length);
}

//var2是字符數組開始截取的位置,從0開始;var3是截取的長度
public String(char[] var1, int var2, int var3) {
    if (var2 < 0) {
         throw new StringIndexOutOfBoundsException(var2);
     } else {
         if (var3 <= 0) {
             if (var3 < 0) {
                 throw new StringIndexOutOfBoundsException(var3);
             }

             if (var2 <= var1.length) {
                 this.value = "".value;
                 return;
             }
         }

         if (var2 > var1.length - var3) {
             throw new StringIndexOutOfBoundsException(var2 + var3);
         } else {
             this.value = Arrays.copyOfRange(var1, var2, var2 + var3);
         }
     }
 }
複製代碼

一樣都是用字符數組建立 String,前者是複製完整的字符數組到 String 中的 value 值,後者是從截取字符數組的一部份內容複製到 String 中。使用 Arrays.copyOf 方法或 Arrays.copyOfRange 方法進行復制,建立一個新的字符串對象,隨後修改的字符數組不影響新建立的字符串。安全

四、參數爲 int 數組

    public String(int[] var1, int var2, int var3) {
        if (var2 < 0) {
            throw new StringIndexOutOfBoundsException(var2);
        } else {
            if (var3 <= 0) {
                if (var3 < 0) {
                    throw new StringIndexOutOfBoundsException(var3);
                }

                if (var2 <= var1.length) {
                    this.value = "".value;
                    return;
                }
            }

            if (var2 > var1.length - var3) {
                throw new StringIndexOutOfBoundsException(var2 + var3);
            } else {
                int var4 = var2 + var3;
                int var5 = var3;

                int var7;
                for(int var6 = var2; var6 < var4; ++var6) {
                    var7 = var1[var6];
                    if (!Character.isBmpCodePoint(var7)) {//判斷是否爲負數,正數爲true
                        if (!Character.isValidCodePoint(var7)) {
                            throw new IllegalArgumentException(Integer.toString(var7));
                        }

                        ++var5;
                    }
                }

                char[] var10 = new char[var5];
                var7 = var2;

                for(int var8 = 0; var7 < var4; ++var8) {
                    int var9 = var1[var7];
                    if (Character.isBmpCodePoint(var9)) {
                        var10[var8] = (char)var9;
                    } else {
                        Character.toSurrogates(var9, var10, var8++);
                    }

                    ++var7;
                }

                this.value = var10;
            }
        }
    }
複製代碼

須要注意的是:做爲參數的 int 數組中值,至少須要知足「大寫字母(A-Z):65 (A)~ 90(Z);小寫字母(a-z):97(a) ~ 122(z);字符數字(‘0’ ~ ‘9’):48(‘0’) ~ 57(‘9’)」的條件。當數組中值爲其餘數字時,獲得的字符串結果可能爲空或特殊符號。

五、參數爲字節數組

在 Java 中,String 實例中保存有一個 char[] 字符數組,char[] 字符數組是以 unicode 碼來存儲的,String 和 char 爲內存形式。

byte 是網絡傳輸或存儲的序列化形式,因此在不少傳輸和存儲的過程當中須要將 byte[] 數組和 String 進行相互轉化。因此 String 提供了一系列重載的構造方法來將一個字符數組轉化成 String,提到 byte[] 和 String 之間的相互轉換就不得不關注編碼問題。

String(byte[] bytes, Charset charset)
複製代碼

該構造方法是指經過 charset 來解碼指定的 byte 數組,將其解碼成 unicode 的 char[] 數組,構形成新的 String。

這裏的 bytes 字節流是使用 charset 進行編碼的,想要將他轉換成 unicode 的 char[] 數組,而又保證不出現亂碼,那就要指定其解碼方式。

經過字節數組構造 String 有不少形式,會使用 StringCoding.decode 方法進行解碼,按照是否指定解碼方式分的話能夠分爲兩種:

a、

public String(byte[] var1, int var2, int var3) {
   checkBounds(var1, var2, var3);
     this.value = StringCoding.decode(var1, var2, var3);
 }

 public String(byte[] var1) {
     this((byte[])var1, 0, var1.length);
 }
複製代碼

這兩種構造方法沒有指定編碼格式,默認使用 ISO-8859-1 編碼格式進行編碼操做。

    static char[] decode(byte[] var0, int var1, int var2) {
        String var3 = Charset.defaultCharset().name();

        try {
            return decode(var3, var0, var1, var2);
        } catch (UnsupportedEncodingException var6) {
            warnUnsupportedCharset(var3);

            try {
                return decode("ISO-8859-1", var0, var1, var2);
            } catch (UnsupportedEncodingException var5) {
                MessageUtils.err("ISO-8859-1 charset not available: " + var5.toString());
                System.exit(1);
                return null;
            }
        }
    }
複製代碼

b、

String(byte bytes[], Charset charset)
String(byte bytes[], String charsetName)
String(byte bytes[], int offset, int length, Charset charset)
String(byte bytes[], int offset, int length, String charsetName)
複製代碼

當構造方法參數中帶有 charsetName 或者 charset 的時候,使用的解碼的字符集就是咱們指定的 charsetName 或者 charset。

六、參數爲 StringBuilder 或 StringBuffer

    public String(StringBuffer var1) {
        synchronized(var1) {
            this.value = Arrays.copyOf(var1.getValue(), var1.length());
        }
    }

    public String(StringBuilder var1) {
        this.value = Arrays.copyOf(var1.getValue(), var1.length());
    }
複製代碼

這兩個構造方法是不多用到的,平時多使用 StringBuffer.toString 方法或者 StringBuilder.toString 方法。其中 StringBuffer.toString 是調用 String(char[] var1, boolean var2);而 StringBuilder.toString 則是調用 String(char[] var1, int var2, int var3)。關於效率問題,Java 的官方文檔有提到說使用 StringBuilder 的 toString 方法會更快一些,緣由是 StringBuffer 的 toString 方法是 synchronized 的,在犧牲了效率的狀況下保證了線程安全。

七、特殊的 protected 構造方法

    String(char[] var1, boolean var2) {
        this.value = var1;
    }
複製代碼

從代碼中咱們能夠看出,該方法和 String(char[] value)有兩點區別:

  • 第一個區別:該方法多了一個參數:boolean var2,這個參數上述咱們也提到過,在 StringBuffer.toString 方法中有使用過,當時傳值爲 true。可是實際使用過程當中能夠經過該構造函數直接進行字符串對象的生成,加入這個 var2 的能夠有效區分於 String(char[] value) 方法,不加這個參數就沒辦法定義這個函數,只有參數是不一樣才能進行重載。
  • 第二個區別:具體的方法實現不一樣。咱們前面提到過 String(char[] value) 方法在建立 String 的時候會用到 Arrays 的 copyOf 方法將 value 中的內容逐一複製到 String 當中,而這個 String(char[] var1, boolean var2) 方法則是直接將 var1的引用賦值給 String 的 value。那麼也就是說,這個方法構造出來的 String 和參數傳過來的 char[] var1共享同一個數組。

在網上看到說是有兩點區別,關於第二點區別,關鍵在於調用方調用該構造方法前是怎麼處理的,在 StringBuffer.toString 方法中能夠詳細的看出 new String(char[] var1, boolean var2) 的使用。

    public synchronized String toString() {
        if (this.toStringCache == null) {
            this.toStringCache = Arrays.copyOfRange(this.value, 0this.count);
        }

        return new String(this.toStringCache, true);
    }
複製代碼

其中 toStringCache 表示緩存,用來保存上一次調用 toString 的結果,若是 value 的字符串序列發生改變,就會將它清空。首先判斷 toStringCache 是否爲 null,若是是先將 value 經過 Arrays.copyOfRange 方法複製到緩存裏,而後使用 toStringCache new一個 String。

綜上,我認爲該構造方法雖然沒法拿來直接使用,但是在別的地方可使用,好比說 StringBuffer.toString 方法中。至於爲什麼要再構建這麼一個特殊的構造方法,而不是直接使用 String(char[] value) 方法,緣由在於 StringBuffer 中的 toStringCache 屬性存在,它的意義不支持在 toString 方法中直接使用 String(char[] value) 方法。(該部分僅爲我的觀點,若有差錯,請指正!)

爲何 Java 會提供這樣一個方法呢?

  • 性能好:一個是直接給數組賦值(若是 StringBuffer 的 value 字符串序列不發生改變,僅須要複製一次便可),一個是逐一拷貝,固然是直接賦值快了。
  • 節約內存:該方法之因此設置爲 protected,是由於一旦該方法設置爲公有,在外面能夠訪問的話,若是構造方法沒有對 arr 進行拷貝,那麼其餘人就能夠在字符串外部修改該數組,因爲它們引用的是同一個數組,所以對 arr 的修改就至關於修改了字符串,那就破壞了字符串的不可變性。
  • 安全的:對於調用他的方法來講,因爲不管是原字符串仍是新字符串,其 value 數組自己都是 String 對象的私有屬性,從外部是沒法訪問的,所以對兩個字符串來講都很安全。

講到這裏,就須要講下 String 類中還有沒有其餘的方法像這個構造函數那樣「性能好的、節約內存的、安全」。其實在 Java7 以前 String 類中也有不少這樣的方法,好比 substring,replace,concat,valueOf 等方法,實際上它們使用的是 String(int var1, int var2, char[] var3)方法來實現。

可是在 Java 7 以後,substring 已經再也不使用這種「優秀」的方法了,如下是 Java8 中的源碼:

    public String substring(int var1) {
        if (var1 < 0) {
            throw new StringIndexOutOfBoundsException(var1);
        } else {
            int var2 = this.value.length - var1;
            if (var2 < 0) {
                throw new StringIndexOutOfBoundsException(var2);
            } else {
                return var1 == 0 ? this : new String(this.value, var1, var2);
            }
        }
    }
複製代碼

Java8 中 substring 方法涉及到的 new String 源碼以下:

    public String(char[] var1, int var2, int var3) {
        if (var2 < 0) {
            throw new StringIndexOutOfBoundsException(var2);
        } else {
            if (var3 <= 0) {
                if (var3 < 0) {
                    throw new StringIndexOutOfBoundsException(var3);
                }

                if (var2 <= var1.length) {
                    this.value = "".value;
                    return;
                }
            }

            if (var2 > var1.length - var3) {
                throw new StringIndexOutOfBoundsException(var2 + var3);
            } else {
                this.value = Arrays.copyOfRange(var1, var2, var2 + var3);
            }
        }
    }
複製代碼

會對傳進來的 value 值經過 Arrays.copyOfRange 方法進行拷貝。

反觀 Java6 中 substring 方法涉及到的 new String 源碼以下:

    String(int var1, int var2, char[] var3) {
        this.value = var3;
        this.offset = var1;
        this.count = var2;
    }
複製代碼

同 new String(char[] var1, boolean var2) 構造函數的第二個特色同樣,構造出來的 String 和參數傳過來的 char[] value 共享同一個數組 。這就可能會形成內存泄漏問題。

看一個例子,假設一個方法從某個地方(文件、數據庫或網絡)取得了一個很長的字符串,而後對其進行解析並提取其中的一小段內容,這種狀況常常發生在網頁抓取或進行日誌分析的時候。

下面是示例代碼:

String aLongString = "...averylongstring...";
String aPart = aLongString .substring(2040);    //aPart字符串共享aLongString部分數據
return aPart;
複製代碼

在這裏 aLongString 只是臨時的,真正有用的是 aPart,其長度只有 20 個字符,可是它的內部數組倒是從 aLongString 那裏共享的,所以雖然 aLongString 自己能夠被回收,但它的內部數組卻不能釋放。這就致使了內存泄漏。若是一個程序中這種狀況常常發生有可能會致使嚴重的後果,如內存溢出,或性能降低。

新的實現雖然損失了性能,並且浪費了一些存儲空間,但卻保證了字符串的內部數組能夠和字符串對象一塊兒被回收,從而防止發生內存泄漏,所以新的 substring 比原來的更健壯。

其餘方法

length() 返回字符串長度
isEmpty() 返回字符串是否爲空
charAt(int index) 返回字符串中第(index+1)個字符(數組索引)
char[] toCharArray() 轉化成字符數組
trim()去掉兩端空格
toUpperCase()轉化爲大寫
toLowerCase()轉化爲小寫
boolean matches(String regex) 判斷字符串是否匹配給定的regex正則表達式
boolean contains(CharSequence s) 判斷字符串是否包含字符序列 s
String[] split(String regex, int limit) 按照字符 regex將字符串分紅 limit 份
String[] split(String regex) 按照字符 regex 將字符串分段
複製代碼

一、concat 鏈接函數

    public String concat(String var1) {
        int var2 = var1.length();
        if (var2 == 0) {
            return this;
        } else {
            int var3 = this.value.length;
            char[] var4 = Arrays.copyOf(this.value, var3 + var2);
            var1.getChars(var4, var3);
            return new String(var4, true);
        }
    }
複製代碼

拼接 var1 會生成一個新的字符串對象,對原有字符串無影響。

二、getBytes

byte[] getBytes() 使用平臺的默認字符集將此 String 編碼爲 byte 序列,並將結果存儲到一個新的 byte 數組中。
byte[] getBytes(String var1)     使用指定的字符集將此 String 編碼爲 byte 序列,並將結果存儲到一個新的 byte 數組中。
byte[] getBytes(Charset var1)        使用給定的 charset 將此 String 編碼到 byte 序列,並將結果存儲到新的 byte 數組。
void getBytes(int var1, int var2, byte[] var3, int var4)    已過期
複製代碼

值得注意的是,在使用這些方法的時候必定要注意編碼問題。好比:String s = "你好,世界!"; byte[] bytes = s.getBytes();這段代碼在不一樣的平臺上運行獲得結果是不同的。因爲沒有指定編碼方式,因此在該方法對字符串進行編碼的時候就會使用系統的默認編碼方式。

在中文操做系統中可能會使用 GBK 或者 GB2312 進行編碼,在英文操做系統中有可能使用 iso-8859-1 進行編碼。這樣寫出來的代碼就和機器環境有很強的關聯性了,爲了不沒必要要的麻煩,要指定編碼方式。

三、equals 和 hashCode

    public boolean equals(Object var1) {
        if (this == var1) {
            return true;
        } else {
            if (var1 instanceof String) {
                String var2 = (String)var1;
                int var3 = this.value.length;
                if (var3 == var2.value.length) {
                    char[] var4 = this.value;
                    char[] var5 = var2.value;

                    for(int var6 = 0; var3-- != 0; ++var6) {
                        if (var4[var6] != var5[var6]) {
                            return false;
                        }
                    }

                    return true;
                }
            }

            return false;
        }
    }

    public int hashCode() {
        int var1 = this.hash;
        if (var1 == 0 && this.value.length > 0) {
            char[] var2 = this.value;

            for(int var3 = 0; var3 < this.value.length; ++var3) {
                var1 = 31 * var1 + var2[var3];
            }

            this.hash = var1;
        }
複製代碼

Java 中基於各類數據類型分析 == 和 equals 的區別一節中講到 String 類有重寫本身的 equals 和 hashCode 方法,因此它倆須要一塊兒講述。

首先是 equals 方法,它比較的流程是:字符串對象相同(即自我比較);類型一致且長度相等時,比較字符內容是否相同。

而後是 hashCode 方法,若是 hash 值不等於 0,且 value.length 大於 0,則進行 hash 值計算。這裏重點說下 var1 == 0 這一斷定條件,var1 是一個 int 類型的值,默認值爲 0,所以 0 能夠表示可能未執行過 hash 計算,但不能表示必定未執行過 hash 計算,緣由是咱們如今還不肯定 hash 計算後是否會產生 0 值;

執行 hash 計算後,會不會產生值爲 0 的 hash呢?根據 hash 的計算邏輯,當 val2[0] = 0 時,根據公式 var1 = 31 * var1 + val2[i]; 進行計算, var1 的值等於 0。可是通過查詢 ASCII 表發現,null 的 ASCII 值爲 0 。顯然 val2[0]中永遠不可能存放 null,所以 hash 計算後不會產生 0 值, var1== 0 能夠做爲是否進行過 hash 計算的斷定條件。

最後獲得計算公式爲:

val2[0]*31^(n-1) + val2[1]*31^(n-2) + ... + val2[n-1]  
複製代碼

爲何要使用這個公式,就是在存儲數據計算 hash 地址的時候,咱們但願儘可能減小有一樣的 hash 地址。若是使用相同 hash 地址的數據過多,那麼這些數據所組成的 hash 鏈就更長,從而下降了查詢效率。
因此在選擇係數的時候要選擇儘可能長的係數而且讓乘法儘可能不要溢出的係數,由於若是計算出來的 hash 地址越大,所謂的「衝突」就越少,查找起來效率也會提升。

選擇31做爲因子的緣由: 爲何 String hashCode 方法選擇數字31做爲乘子

有這樣一道面試題「兩個對象的 hashCode()相同,則 equals()也必定爲 true,對嗎?」答案:不對,兩個對象的 hashCode()相同,equals()不必定 true。

String str1 = "通話";
String str2 = "重地";
System.out.println(String.format("str1:%d | str2:%d", str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));

//結果
str1:1179395 | str2:1179395
false
複製代碼

很顯然「通話」和「重地」的 hashCode() 相同,然而 equals() 則爲 false,由於在散列表中,hashCode()相等即兩個鍵值對的哈希值相等,然而哈希值相等,並不必定能得出鍵值對相等。

四、比較方法

boolean equals(Object anObject); 比較對象
boolean contentEquals(StringBuffer sb); 與StringBuffer對象比較內容
boolean contentEquals(CharSequence var1); 與字符比較內容
boolean equalsIgnoreCase(String anotherString);忽略大小寫比較字符串對象
int compareTo(String anotherString); 比較字符串
int compareToIgnoreCase(String str); 忽略大小寫比較字符串
boolean regionMatches(int toffset, String other, int ooffset, int len)局部匹配
boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) 可忽略大小寫局部匹配
複製代碼

contentEquals 方法

    public boolean contentEquals(CharSequence var1) {
        if (var1 instanceof AbstractStringBuilder) {
            if (var1 instanceof StringBuffer) {
                synchronized(var1) {
                    return this.nonSyncContentEquals((AbstractStringBuilder)var1);
                }
            } else {
                return this.nonSyncContentEquals((AbstractStringBuilder)var1);
            }
        } else if (var1 instanceof String) {
            return this.equals(var1);
        } else {
            char[] var2 = this.value;
            int var3 = var2.length;
            if (var3 != var1.length()) {
                return false;
            } else {
                for(int var4 = 0; var4 < var3; ++var4) {
                    if (var2[var4] != var1.charAt(var4)) {
                        return false;
                    }
                }
                return true;
            }
        }
    }
複製代碼

String 、StringBuilder、StringBuffer 都實現了 CharSequence 接口,因此上述方法能夠接收這三種類型的參數。另外 StringBuilder、StringBuffer 繼承了 AbstractStringBuilder 父類,因此它倆經過 nonSyncContentEquals 方法進行比較,注意 StringBuffer 須要考慮線程安全,加鎖以後再調用。

compareTo 方法

    public int compareTo(String var1) {
        int var2 = this.value.length;
        int var3 = var1.value.length;
        int var4 = Math.min(var2, var3);
        char[] var5 = this.value;
        char[] var6 = var1.value;

        for(int var7 = 0; var7 < var4; ++var7) {
            char var8 = var5[var7];
            char var9 = var6[var7];
            if (var8 != var9) {
                return var8 - var9;
            }
        }

        return var2 - var3;
    }
複製代碼

經過下面這個例子進行展現:

String s1 = new String("abc");
String s2 = new String("abcdfg");
System.out.println(s2.compareTo(s1));    //3
System.out.println(s1.compareTo(s2));    //-3
s2 = new String("fghjkl");
System.out.println(s2.compareTo(s1));    //5
複製代碼

當兩個對象內容徹底一致時,返回結果爲 0;在字符串最小長度下,若是有不一樣的字符,系統則自動轉換爲 int 類型作差值。

五、先後綴斷定

boolean startsWith(String var1, int var2)     測試此字符串從指定索引開始的子字符串是否以指定前綴開始
boolean startsWith(String var1)     測試此字符串是否以指定的前綴開始。
boolean endsWith(String var1)        測試此字符串是否以指定的後綴結束。
複製代碼

六、索引獲取

int indexOf(int var1)        返回指定字符(int 轉 char)在此字符串中第一次出現處的索引。從0索引開始
int indexOf(int var1, int var2)    返回在此字符串中第一次出現指定字符處的索引,從指定的索引開始搜索。var2 小於字符串的長度
int indexOf(String var1)        返回指定子字符串在此字符串中第一次出現處的索引,從0索引開始。var1必須是此字符串的一個連續子集,不然返回-1
int indexOf(String var1, int var2)        返回指定子字符串在此字符串中第一次出現處的索引,從指定的索引開始。

int lastIndexOf(int var1)    返回指定字符(int 轉 char)在此字符串中最後一次出現處的索引。從0索引開始
int lastIndexOf(int var1, int var2)    返回在此字符串中最後一次出現指定字符處的索引,從指定的索引開始搜索。var2 小於字符串的長度
int lastIndexOf(String var1)    返回指定子字符串在此字符串中最後一次出現處的索引,從0索引開始。var1必須是此字符串的一個連續子集,不然返回-1
int lastIndexOf(String var1, int var2)    返回指定子字符串在此字符串中最後一次出現處的索引,從指定的索引開始。
複製代碼

七、字符串截取

String substring(int var1)    返回一個新的字符串,它是此字符串的一個子字符串。從var1開始截取,截取長度爲此字符串的長度減去var1
String substring(int var1, int var2)    返回一個新字符串,它是此字符串的一個子字符串。從var1開始截取,截取長度爲var2。
CharSequence subSequence(int var1, int var2)        返回一個新的字符序列,它是此序列的一個子序列。
複製代碼

八、字符串替換

String replace(char oldChar, char newChar)     返回一個新的字符串,它是經過用 newChar 替換此字符串中出現的全部 oldChar 獲得的。
String replaceFirst(String regex, String replacement)         使用給定的 replacement 替換此字符串匹配給定的正則表達式的第一個子字符串。
String replaceAll(String regex, String replacement)     使用給定的 replacement 替換此字符串全部匹配給定的正則表達式的子字符串。
String replace(CharSequence var1, CharSequence var2)    使用指定的字面值替換序列替換此字符串全部匹配字面值目標序列的子字符串。
複製代碼
    public String replace(char var1, char var2) {
        if (var1 != var2) {
            int var3 = this.value.length;
            int var4 = -1;
            char[] var5 = this.value;

            do {
                ++var4;
            } while(var4 < var3 && var5[var4] != var1);

            if (var4 < var3) {
                char[] var6 = new char[var3];

                for(int var7 = 0; var7 < var4; ++var7) {
                    var6[var7] = var5[var7];
                }

                while(var4 < var3) {
                    char var8 = var5[var4];
                    var6[var4] = var8 == var1 ? var2 : var8;
                    ++var4;
                }

                return new String(var6, true);
            }
        }

        return this;
    }
複製代碼

replace 的參數能夠是 char 或者 CharSequence,便可以支持字符的替換, 也支持字符串的替換。當參數爲 char 時,是經過本身自定義的方法來更換字符;當參數爲 CharSequence 時,實際調用的是 replaceAll 方法。replaceAll 和 replaceFirst 的參數是 regex,即基於規則表達式的替換。區別是一個所有替換,一個只替換第一個。

九、valueOf

static String valueOf(Object var0)
static String valueOf(char[] var0)
static String valueOf(char[] var0, int var1, int var2)
static String valueOf(boolean var0)
static String valueOf(int var0)
static String valueOf(long var0)
static String valueOf(float var0)
static String valueOf(double var0)
複製代碼

valueOf 都是靜態函數,不須要實例化 String 對象,直接調用用於將其餘基本數據類型轉換爲 String 類型。

十、intern()方法

public native String intern();
複製代碼

intern 方法是 Native 調用,它的做用是在全局字符串常量池裏尋找等值的對象的引用,若是沒有找到則在常量池中存放當前字符串對象的引用並返回該引用,不然直接返回常量池中已存在的 String 對象引用。

intern 方法與常量池有着很大的聯繫,經過學習該方法的使用,便於咱們瞭解內存分配的概念,因此該方法會在後續章節裏詳細講解。想要了解的朋友能夠先了解一下常量池的知識,前往 Java 中方法區與常量池 便可。

參考連接

相關文章
相關標籤/搜索