Ch.9 字串

9-1 String 類別

在 Java 程式中經常大量的使用字串,字串在 Java 執行過程中為 java.lang.String 類別的一個實例,String 類別是 Java 標準類別庫眾多類別的其中之一,能夠提供您儲存一連串的字元(陣列)。

• 不使用 new 關鍵字建立 String 物件

其實您經常使用 String 物件,在您撰寫像"Hello!World!"這樣的第一個 Java 程式時,您就使用了 String 物件,因為在 Java 中,以雙引號包括的文字,編譯器會建立 String 物件來表示該文字:

System.out.println("Hello!World!");
                            

如何看出它是個物件,很簡單!即然是物件,就擁有一些可操作的方法,例如 String 物件上有個 toUpperCase()方法,可以將字串中的所有英文字母字元變為大寫字母字元後傳回,所以下面的程式碼片段將顯示"HELLO!WORLD!":

System.out.println("Hello!World!".toUppercase()) ;
                            

每次都使用雙引號來包括文字以得到 String 實例,在撰寫上過於麻煩,您可以使用 String 類別來宣告一個參考名稱,參考至您所建立的 String 實例,例如:

String text = "Hello!World!";
                            

之後直接使用參考名稱來操作所參考的 String 實例,例如:

// 顯示"Hello!World!"
System.out.println(text);
// 顯示"HELLO!WORLD!"
System.out.println(text.toUpperCase());
                            

直接使用雙引號包括所產生的 String 物件,在程式運行期間將一直存在於 String 儲存池中,雙引號包括的字串只要是字元內容相同,都會重複使用 String 儲存池中的同一個 String 物件,您可以使用程式碼 9-1 來作個簡單的驗證。

public class StringDemo{
    public static void main(String[] args){
        String text1 = "Hello!";
        String text2 = "Hello!";

        System.out.println("Hello!" == "Hello!");
        System.out.println(text1 == "Hello!");
        System.out.println(text2 == "Hello!");
        System.out.println(text1 == text2);
    }
}
                            
程式碼 9-1 StringDemo.java

這邊要提醒一下,當關係運算子"=="使用於兩個物件之比較時,目的是比較兩個物件是否為同一個物件,也就是比較是否存在同一個記憶體位置,在程式運行時,程式碼 9-1 中的"Hello!"只會有一個實例,所以第 6 行到第 9 行的比較,結果都會是 true,如下所示:

圖9-1 程式碼 9-1 的執行結果

使用雙引號包括的文字,是被固定寫在程式碼中的,所以運行時會只存在一個 String 實例並被重複使用,以節省記憶體空間並增進程式執行效率。

圖9-2 程式碼 9-1 中的 text1、text2 參考至同一實例

• 使用 new 關鍵字建立 String 物件

有很多方法可以建立 String 物件,其中一個方法是使用 new 關鍵字來建立 String 物件並且同時定義字串來初始化物件:

String name = new String("Justin Lin");
                            

上面的程式碼片段將以"Justin Lin"字串的字元內容為基礎,建立一個「新」的 String 物件,注意到這邊說的是「新」的 String 物件,name 所參考的並不是括號中的"Justin Lin" 物件,可以使用程式碼 9-2 來作個簡單的印證。

public class NewString{
    public static void main(String[] args){
        String name1 = "Justin Lin";
        String name2 = new String("Justin Lin");

        System.out.println(name1 == "Justin Lin");
        System.out.println(name2 == "Justin Lin");
        System.out.println(name1 == name2);
    }
}
                            
程式碼 9-2 NewString.java

直接在 Java 程式中使用雙引號包括的文字,在運行時會以一個 String 實例存在於 String 儲存池中,而使用 new 建立的 String 實例則是運行時產生,所以兩者不會是同一個實例,程式碼第 4 行是根據"Justin Lin"的字元內容另行建立的String 實例,所以 name1、name2 以==作比較時,結果會是 false。

圖9-3 程式碼 9-2 中的 name1 與 name2 參考至不同實例

程式碼 9-2 的執行結果如下所示:

圖9-4 程式碼 9-2 執行結果

同樣的,如果您使用 new 分別建立 String 物件,即使它們的字元內容相同,但事實上仍是不同的兩個物件實例,例如以下的程式碼片段將顯示 false 的結果:

String name1 = new String("Justin Lin");
String name2 = new String("Justin Lin");
System.out.println(name1 == name2);
                            

您也可以使用字元陣列為基礎,並使用 new 關鍵字來建立String 實例,例如下面的程式碼片段將建立"Justin"的 String 實例:

char[] array = {'J', 'u', 's', 't', 'i', 'n'};
String name = new String(array);
                            

您也可以指定字元陣列中第幾個字元開始,使用之後的指定字元個數來建立 String 實例,例如下面的程式碼片段從字元陣列的索引 0 開始,擷取 4 個字元來建立 String 實例,結果會顯示"Just":

char[] array = {'J', 'u', 's', 't', 'i', 'n'};
String name = new String(array, 0, 4);
System.out.println(name);
                            

重點提示

除了這邊介紹的幾個 String 建構方法之外,還有數個未介紹的 String 建構方式,這些建構的方式是進階議題,建議您參考一下 java.lang.String 的線上 API 文件說明:http://docs.oracle.com/javase/7/docs/api/index.html?java/lang/String.html

• String 實例上的方法

String 實例上擁有許多方法可以操作,在這邊先介紹一些簡單的方法操作,像是 String 的大小寫轉換、取得指定的子字串、字元相關操作及字串的搜尋等。

大小寫轉換

您可以將 String 實例的英文字母字元內容全部轉為大寫字母或小寫字母,並以新的 String 實例傳回,只要使用toUpperCase()或 toLowerCase()方法即可,例如:

public class StringMethod{
    public static void main(String[] args) {
        String name = "Java";
        String upper = name.toUpperCase();
        String lower = name.toLowerCase();

        System.out.println("原字串:" + name);
        System.out.println("轉大寫:" + upper);
        System.out.println("轉小寫:" + lower);
    }
}
                            
程式碼 9-3 StringMethod.java

要注意到 toUpperCase()與 toLowerCase()方法以原來的String 物件之字元內容為基礎,傳回一個新的且已轉為大寫或小寫的 String 實例,新的實例與原來的 String 物件是沒有關係,執行結果如圖 9-5 所示:

圖9-5 程式碼 9-3 執行結果

取得子字串

您可以從 String 實例上指定字元開始位置,然後傳回指定的子字串,例如下面的程式片段會顯示"Lin":

System.out.println("Justin Lin".substring(7));
                            

注意字元的位置指定是從 0 開始,您也可以指定字串的起始索引與終止索引,例如下面的程式片段會顯示"Justin":

System.out.println("Justin Lin".substring(0, 6));
                            

使用這個版本的 substring()指定範圍時,傳回的 String 物件將包括起始索引的字元,不包括終止索引的字元

字元操作

字串是由字元所組成,您可以使用 charAt()方法指定索引,以傳回對應的字元,注意索引是從 0 開始,舉個實際的例子,程式碼 9-4 會在每一行顯示字串的內容:

public class CharAtDemo{
    public static void main(String[] args){
        String name = "Java";

        for (int i = 0; i < name.length(); i++){
            System.out.println(name.charAt(i));
        }
    }
}
                            
程式碼 9-4 CharAtDemo.java

String 物件的 length()方法會傳回字串的字元長度,執行結果如下所示:

圖9-6 程式碼 9-4 執行結果

您也可以使用 String 物件的 toCharArray()方法以字元陣列傳回字串的字元組成,例如程式碼 9-5 以字元陣列顯示的方式來改寫程式碼 9-4。

public class CharDemo{
    public static void main(String[] args){
        String name = "Java";
        char[] array = name.toCharArray();

        for (char c : array){
            System.out.println(c);
        }
    }
}
                            
程式碼 9-4 CharDemo.java

程式的第 4 行取得字元陣列,並在程式的第 6 行使用 foreach 語法,循序顯示字元陣列的內容,執行結果與程式碼 9-5 是一樣的,可參考圖 9-6 的畫面。

搜尋比對

字串的搜尋比對在程式設計中是一個很常遇到的需求,在Java 中您只要操作對應的搜尋方法,就可以完成一些搜尋比對的執行結果,您不用花心思去設計字串搜尋演算法,這是 Java 中操作 String 物件的好處之一。

String 物件上與字串搜尋比對的方法相當的多,表 9-1 先列出一些常用的方法以供參考:

方法名稱說明
int compareTo(String s)依字典順序比較兩個字串的字元內容,傳回 0 表示順序相同,負值表示 String 物件順序在 s 前,正值表示順序在 s 之後
boolean contains(CharSequence s)測試是否包括實作 CharSequence 介面的物件,例如 String 就實作了CharSequence,簡單的說就是測試字串中是否包括 s
boolean endsWith(String s)測試字串是否以 s 作結束
int indexOf(int ch)傳回字串中第一個 ch 字元的索引位置
int indexOf(String s)傳回字串中第一個 s 的起始索引位置
int lastIndexOf(int ch)傳回字串中最後一個 ch 字元的索引位置
int lastIndexOf(String s)傳回字串中最後一個 s 的索引位置
boolean startsWith(String s)測試字串是否以 s 作開始
表 9-1 String 上的搜尋比對方法

程式碼 9-6 簡單的示範了這些方法的使用與執行結果:

public class MatchDemo{
    public static void main(String[] args){
        String text = "Java Everywhere";

        System.out.println("第一個 a 字元:"
                + text.indexOf('a'));
        System.out.println("最後一個 a 字元:"
                + text.lastIndexOf('a'));
        System.out.println("第一個 Every:"
                + text.indexOf("Every"));
        System.out.println("最後一個 Every:"
                + text.lastIndexOf("Every"));
        System.out.println("包括 Java:"
                + text.contains("Java"));
        System.out.println("以 Java 為開始:"
                + text.startsWith("Java"));
        System.out.println("以 Java 為結束:"
                + text.endsWith("Java"));
    }
}
                            
程式碼 9-6 MatchDemo.java

進行字串內容的比對時,記得是有區分字母大小寫的,程式碼 9-6 執行結果如下所示:

圖9-7 程式碼 9-6 執行結果

• 使用 equals()比對字串內容

這邊藉由字串內容相等性的比較,來重新複習一個重要的觀念:「關係運算子==用於比較物件時,是用來比較兩個物件是否為同一物件,如果您要比較兩個物件的相等性,請用equals()方法」。

所以如果您要比較兩個 String 物件的字元內容是否相同,您不可以使用"=="來進行比較,而必須使用 equals()方法,以下直接使用程式碼 9-7 來作示範說明。

public class EqualsDemo{
    public static void main(String[] args){
        String text1 = "Java";
        String text2 = new String("Java");
        String text3 = new String("Java");

        System.out.println(
                "==比較是否為同一物件..");
        System.out.println(text1 == text2);
        System.out.println(text1 == text3);
        System.out.println(text2 == text3);


        System.out.println(
                "\nequals()比較字元內容..");
        System.out.println(text1.equals(text2));
        System.out.println(text2.equals(text3));
        System.out.println(text1.equals(text3));
    }
}
                            
程式碼 9-7 EqualsDemo.java

對於程式碼 9-7 來說,text1、text2、text3 各參考至不同的物件,所以使用==進行比較時,程式碼第 9、10、11 行都會顯示 false 的結果,在 String 上 equals()被定義為比較字元內容是否相同,text1、text2、text3 的 字元內容都是"Java" ,所以程式碼第 16、17、18 行都會顯示 true 的結果,執行結果如下圖所示:

圖9-8 程式碼 9-7 執行結果

equals()方法會比較兩個 String 的字元內容是否相同,且區分大小寫,另外還有 equalsIgnoreCase()方法,可以讓您不區分字元的大小寫來進行比較。