Java 字符串簡(jiǎn)介(學(xué)習(xí) Java 編程語(yǔ)言 011)

從概念上講,Java 字符串就是 Unicode 字符序列。Java 沒有內(nèi)置的字符串類型,而是在標(biāo)準(zhǔn) Java 類庫(kù)中提供了一個(gè)預(yù)定義類,很自然地叫做 String。每個(gè)用雙引號(hào)括起來(lái)的字符串都是 String 類的一個(gè)實(shí)例:

String e = ""; // 空字符串
String greeting = "Hello";

1. 子串

String 類的 substring 方法可以從一個(gè)較大的字符串提取出一個(gè)子串。

String greeting = "Hello";
String s = greeting.substring(0, 3); // 變量 s 為 "Hel"

字符串中的代碼單元和代碼點(diǎn)從 0 開始計(jì)算。substring 方法的第二個(gè)參數(shù)是不想復(fù)制的第一個(gè)位置。

substring 的工作方式有一個(gè)優(yōu)點(diǎn): 容易計(jì)算子串長(zhǎng)度。字符串 s.substring(a, b) 的長(zhǎng)度為 b - a。

2. 拼接

Java 語(yǔ)言允許使用 + 號(hào)連接(拼接)兩個(gè)字符串。

String expletive = "Expletive";
String PG13 = "deleted";
String message = expletive + PG13;
System.out.println(message);        // 打印 Expletivedeleted

當(dāng)將一個(gè)字符串與一個(gè)非字符串的值進(jìn)行拼接時(shí),后者被轉(zhuǎn)換成字符串。

如果需要把多個(gè)字符串放在一起,用一個(gè)界定符分隔,可以使用靜態(tài) join 方法:

String all = String.join(" /", "S", "M", "L", "XL");
System.out.println(all);// 打印 S /M /L /XL

String [] arr = {"S", "M", "L", "XL"};
all = String.join(" /", arr);
System.out.println(all);// 打印 S /M /L /XL

arr = new String[]{"S", "M", "L", "XL"};
List<String> list = Arrays.asList(arr);
all = String.join(" /", arr);
System.out.println(all);// 打印 S /M /L /XL

3. 字符串不可變

String 類沒有提供用于修改字符串的方法。由于不能修改 Java 字符串,所以在 Java 文檔中將 String 類對(duì)象稱為是不可變的(immutable)。不可變字符串卻有一個(gè)優(yōu)點(diǎn):編譯器可以讓字符串共享。

Java 的設(shè)計(jì)者認(rèn)為共享帶來(lái)的高效率遠(yuǎn)遠(yuǎn)勝過于提取子串、拼接字符串所帶來(lái)的低效率。查看一下程序會(huì)發(fā)現(xiàn):很少需要修改字符串,而是往往需要對(duì)字符串進(jìn)行比較(有一種例外情況,將來(lái)自于文件或鍵盤的單個(gè)字符或較短的字符串匯集成字符串),Java 專門為此提供了一個(gè)單獨(dú)的類。

4. 檢測(cè)字符串是否相等

可以使用 equals 方法檢測(cè)兩個(gè)字符串是否相等。表達(dá)式:

s.equals(t);

如果字符串 s 與字符串 t 相等,返回 true;否則,返回 false。s 與 t 可以是字符串變量,也可以是字符串字面量。

想要檢測(cè)兩個(gè)字符串是否相等,而不區(qū)分大小寫,可以使用 equalsIgnoreCase 方法。

"Hello".equalsIgnoreCase("hello"); // true

一定不要使用 == 運(yùn)算符檢測(cè)兩個(gè)字符串是否相等!這個(gè)運(yùn)算符只能夠確定兩個(gè)字符串是否放置在同一個(gè)位置上。當(dāng)然,如果字符串放置在同一個(gè)位置上,她們必然相等。但是,完全有可能將內(nèi)容相同的多個(gè)字符串的拷貝放置在不同的位置上。

如果虛擬機(jī)始終將相同的字符串共享,就可以使用 == 運(yùn)算符檢測(cè)是否相等。但實(shí)際上只有字符串常量是共享的。而 + 或 substring 等操作產(chǎn)生的結(jié)果并不是共享的。因此,千萬(wàn)不要使用 == 運(yùn)算符測(cè)試字符串的相等性,以免在程序中出現(xiàn)糟糕的 bug。這種 bug 很像隨機(jī)產(chǎn)生的間歇性錯(cuò)誤。

5. 空串與 Null 串

空串是一個(gè) Java 對(duì)象,有自己的長(zhǎng)度(0)和內(nèi)容(空)??沾?"" 是長(zhǎng)度為 0 的字符串。

代碼檢查一個(gè)字符串是否為空:

if (str.length() == 0)
// 或
if (str.equals(""))

要檢查一個(gè)字符串是否為 null:

if (str == null)

檢查一個(gè)字符串既不是 null 也不為空串:

if (str != null && str.length() != 0)

6. 碼點(diǎn)與代碼單元

Java 字符串由 char 值序列組成。char 數(shù)據(jù)類型是一個(gè)采用 UTF-16 編碼表示 Unicode 碼點(diǎn)的代碼單元。最常用 Unicode 字符使用一個(gè)代碼單元就可以表示,而輔助字符需要一對(duì)代碼單元表示。

length 方法將返回采用 UTF-16 編碼表示的給定字符所需要的代碼單元數(shù)量。

String greeting = "Hello";
int n = greeting.length(); // 變量 n 為 5

要想得到實(shí)際的長(zhǎng)度,即碼點(diǎn)數(shù)量,可調(diào)用:

String greeting = "Hello";
int n = greeting.codePointCount(0, greeting.length()); // 變量 n 為 5

調(diào)用 s.charAt(n) 將返回位置 n 的代碼單元,n 介于 0 ~ s.length()-1 之間。

String greeting = "Hello";
char first = greeting.charAt(0); // first 為 'H'
char last = greeting.charAt(4); // last 為 'o'

要想得到第 i 個(gè)代碼點(diǎn),應(yīng)該使用下列語(yǔ)句

String greeting = "Hello";
int i = 3;

int index = greeting.offsetByCodePoints(0, i);
int cp = greeting.codePointAt(index);
// 或者
int cp = greeting.codePointAt(index);

Java 對(duì)字符串中的代碼單元和碼點(diǎn)從 0 開始。

為什么對(duì)代碼單元如此大驚小怪?考慮下列語(yǔ)句:

?? is the set of octonions.

使用 UTF-16編碼表示字符 ??(U+1D546) 需要兩個(gè)代碼單元。調(diào)用

char ch = sentence.charAt(1);

返回不是一個(gè)空格,而是 ?? 的第二個(gè)代碼單元。為了避免這個(gè)問題,不要使用 char 類型。這太底層了。

String greeting = "Hello";
// 獲得實(shí)際的長(zhǎng)度,即碼點(diǎn)數(shù)量
int n = greeting.codePointCount(0, greeting.length());
System.out.println(n); // 打印 5

// 輔助字符 ??(U+1D546)
String str = "\ud835\udd46";
System.out.println(str); // 打印 ??

// 獲得輔助字符 ?? 實(shí)際的長(zhǎng)度,即碼點(diǎn)數(shù)量:2
n = str.length();
System.out.println(n); // 打印 2
System.out.println(str.charAt(0)); // 打印 ?
System.out.println(str.charAt(1)); // 打印 ?


// 打印輔助字符 ?? 的碼點(diǎn)
System.out.println(str.codePointAt(0)); // 打印 120134
// 打印輔助字符 ??
System.out.println(new String(Character.toChars(120134))); // 打印 ??

// 打印輔助字符 ?? 的 unicode 字符:\ud835\udd46
for(int i = 0; i < str.length(); i++) {
    String unicode = Integer.toHexString(str.charAt(i));
    System.out.print("\\u");
    System.out.print(unicode);
} 

如果想要遍歷一個(gè)字符串,并且依次查看每一個(gè)碼點(diǎn),可以使用下列語(yǔ)句:

String str = "\ud835\udd46 is the set of octonions.";
int i = 0;
while(i < str.length()) {
    int cp = str.codePointAt(i);
    System.out.print(new String(Character.toChars(cp)));

    if(Character.isSupplementaryCodePoint(cp)) {
        i += 2;
    }else {
        i++;
    }
}

打印:?? is the set of octonions.

可以使用下列語(yǔ)句實(shí)現(xiàn)回退操作:

String str = "\ud835\udd46 is the set of octonions.";
int i = str.length();
while(i > 0) {
    i--;
    if (Character.isSurrogate(str.charAt(i))) {
        i--;
    }
    int cp = str.codePointAt(i);
    System.out.print(new String(Character.toChars(cp)));
}

打?。?code>.snoinotco fo tes eht si ??

顯然,這很麻煩。更容易的辦法是使用 codePoints 方法,它會(huì)生成一個(gè) int 值 “流”,每個(gè) int 值對(duì)應(yīng)一個(gè)碼點(diǎn)??梢詫⑺D(zhuǎn)換為一個(gè)數(shù)組,再完成遍歷。

int[] codePoints = str.codePoints().toArray();

反之,要把一個(gè)碼點(diǎn)數(shù)組轉(zhuǎn)換為一個(gè)字符串,可以使用構(gòu)造函數(shù)。

String str = new String(codePoints, 0, codePoints.length);

虛擬機(jī)不一定吧字符串實(shí)現(xiàn)為代碼單元序列。在 Java 9 中,只包含單字節(jié)代碼單元的字符串使用 byte 數(shù)組實(shí)現(xiàn),所有其他字符串使用 char 數(shù)組。

// str = ?? is the set of octonions. ??????
String str = "\ud835\udd46 is the set of octonions. \ud83c\udf7a\ud83c\udf7a\ud83c\udf7a";
System.out.println(str);

// 正向遍歷字符串 1
for(int i = 0; i < str.length();) {
    int cp = str.codePointAt(i);
    if(Character.isSupplementaryCodePoint(cp)) {
        i += 2;
    }else {
        i++;
    }
    char[] chars = Character.toChars(cp);
    String code = new String(chars);
    System.out.print(code);
}
System.out.println();

// 正向遍歷字符串 2
int[] codePoints = str.codePoints().toArray();
for(int i = 0; i < codePoints.length; i++) {
    String code = new String(Character.toChars(codePoints[i]));
    System.out.print(code);
}
System.out.println();

// 將碼點(diǎn)數(shù)組轉(zhuǎn)化為字符串
String newStr = new String(codePoints, 0, codePoints.length);
System.out.println(newStr);

// 反向遍歷字符串
for(int i = str.length(); i > 0;) {
    i--;
    
    char ch = str.charAt(i);
    if(Character.isSurrogate(ch)) {
        i--;
    }
    int cp = str.codePointAt(i);
    char[] chars = Character.toChars(cp);
    String code = new String(chars);
    System.out.print(code);
}

7. 構(gòu)建字符串

有時(shí)需要由較短的字符串構(gòu)建字符串,例如,按鍵或來(lái)自文件中的單詞。采用字符串連接的方式達(dá)到此目的效率比較低。每次連接字符串,都會(huì)構(gòu)建一個(gè)新的 String 對(duì)象,即耗時(shí),又浪費(fèi)空間。使用 StringBuilder 類就可以避免上述問題的發(fā)生。

// 構(gòu)建一個(gè)空的字符串構(gòu)建器
StringBuilder builder = new StringBuilder();

// 向字符串構(gòu)造器對(duì)象中追加小段字符串
builder.append("Welcome");
builder.append(" ");
builder.append("to");
builder.append(" ");
builder.append("xiang017");
builder.append("!");

// 構(gòu)造字符串對(duì)象
String str = builder.toString();

// 打印 Welcome to xiang017!
System.out.println(str);

StringBuilder 類的前身是 StringBuffer,它的效率稍有些低,但允許采用多線程的方式添加或刪除字符。如果所有字符串編輯操作都在單個(gè)線程中執(zhí)行(通常都是這樣),則應(yīng)該使用 StringBuilder。這兩個(gè)類的 API 是一樣的。

8. StringBuilder 類中的重要方法:

java.lang.StringBuilder

  • StringBuilder()

    構(gòu)造一個(gè)空的字符串構(gòu)建器。

  • int length()

    返回構(gòu)建器或緩沖器中的代碼單元數(shù)量。

  • StringBuilder append(String str)

    追加一個(gè)字符串并返回 this。

  • StringBuiler append(char c)

    追加一個(gè)代碼單元病分會(huì) this。

  • void setCharAt(int i, char c)

    將第 i 個(gè)代碼單元設(shè)置為 c。

  • StringBuilder insert(int offset, String str)

    在 offset 位置插入一個(gè)字符串并返回 this。

  • StringBuilder insert(int offset, char c)

    在 offset 位置插入一個(gè)代碼單元并返回 this。

  • StringBuilder delete(int startIndex, int endIndex)

    刪除變異量從 startIndex 到 endIndex-1 的代碼單元并返回 this。

  • String toString()

    返回一個(gè)與構(gòu)建器或緩沖器內(nèi)容相同的字符串。

9. String 類中的重要方法

Java 中的 String 類包含了 50 多個(gè)方法。它們絕大多數(shù)都很有用,使用的評(píng)率非常高。

java.lang.String

  • char charAt(int index)

    返回給定位置的代碼單元。除非對(duì)底層的代碼單元感興趣,否則不需要調(diào)用這個(gè)方法。

  • int codePointAt(int index) 5

    返回從給定位置開始的碼點(diǎn)。

  • int offsetByCodePoints(int startIndex, int cpCount) 5

    返回從 startIndex 碼點(diǎn)開始,cpCount 個(gè)碼點(diǎn)后的碼點(diǎn)索引。

  • int compareTo(String other)

    按照字典順序,如果字符串位于 other 之前,返回一個(gè)負(fù)數(shù);如果為字符串 other 之后,返回一個(gè)整數(shù);如果兩個(gè)字符串相等,返回 0。

  • IntString codePoints() 8

    將這個(gè)字符串的碼點(diǎn)作為一個(gè)流返回。調(diào)用 toArray 將它們放在一個(gè)數(shù)組中。

  • new String(int[] codePoints, int offset, int count) 5

    用數(shù)組中從 offset 開始的 count 個(gè)碼點(diǎn)構(gòu)造一個(gè)字符串。

  • boolean empty()

    如果字符串為空,返回 true。

  • boolean blank() 11

    如果字符串由空字符串組成,返回 true。

  • boolean equals(Other other)

    如果字符串與 other 相等,返回 true。

  • boolean equalsIgnoreCase(String other)

    如果字符串與 other 相等(忽略大小寫),返回 true。

  • boolean startsWith(String prefix)

    如果字符串以 prefix 開頭,返回 true。

  • boolean endWith(String suffix)

    如果字符串以 suffix 結(jié)尾,返回 true。

  • int indexOf(String str)
  • int indexOf(String str, int fromIndex)
  • int indexOf(int cp)
  • int indexOf(int cp, int fromIndex)

    返回與字符串 str 或碼點(diǎn) cp 匹配的第一個(gè)子串的開始位置。從索引 0 或 fromIndex 開始匹配。如果在原始字符串中不存在 str 或 cp,則返回 -1。

  • int lastIndexOf(String str)
  • int lastIndexOf(String str, int fromIndex)
  • int lastIndexOf(int cp)
  • int lastIndexOf(int cp, int fromIndex)

    返回與字符串 str 或碼點(diǎn) cp 匹配的最后一個(gè)子串的開始位置。從原始字符串末尾或 fromIndex 開始匹配。

  • int length()

    返回字符串代碼單元的個(gè)數(shù)。

  • int codePointCount(int startIndex, int endIndex) 5

    返回 startIndex 和 endIndex-1 之間的碼點(diǎn)個(gè)數(shù)。

  • String replace(CharSequence oldString, CharSequence newString)

    返回一個(gè)新的字符串。這個(gè)字符串用 newString 代替原始字符串中所有的 oldString。可以用 String 或 StringBuilder 對(duì)象作為 CharSequence 參數(shù)。

  • String substring(int beginIndex)
  • String substring(int beginIndex, int endIndex)

    返回一個(gè)新字符串。這個(gè)字符串包含原始字符串從 beginIndex 到字符串末尾或 endIndex-1 的素有代碼單元。

  • String toLowerCase()

    返回一個(gè)新的字符串。這個(gè)字符串將原始字符串中的大寫字母改為小寫。

  • String toUpperCase()

    返回一個(gè)新的字符串。這個(gè)字符串將原始字符串中的小寫字母改為大寫。

  • String trim()
  • String strip() 11

    返回一個(gè)新字符串。這個(gè)字符串將刪除原始字符串頭部和尾部小于等于 U+0020 的字符(trim)或空格(strip)。

  • String join(CharSequence delimiter, CharSequence... elements) 11

    返回一個(gè)新字符串,用給定的定界符連接所有元素。

  • String repeat(int count) 11

    返回一個(gè)字符串,當(dāng)前字符串重復(fù) count 次。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容