5-1  字串的使用

        字串是一種使用非常頻繁的資料型態。字串可以被視為一連串的字元所組合而成的資料型態。如果你參照JDK說明文件的內容,雖然您所看到的字串是以char[]型態來表示,但在Java中,字串和字元是屬於完全不同的資料型別:「字元」是屬於「Character」類別或是「char」基本資料型態,而「字串」是屬於「String」類別,該類別是位在「java.lang」套件下。在「String」類別中擁有許多的方法可以被用來處理字串。

由於字串內部事由字元陣列所組合而成的,因此,字串中的各個字元的排列方式也是和字元陣列相同。例如:字串「Java」的排列方式是:

J

a

v

a

                 [0]                                  [1]                                  [2]                                  [3]                 

了解字串的這個特性將會對您在操作字串上會有很大的幫助。

◎初始及定義String物件

        字串物件的建構方式多達13種。在使用上,我們可以使用字串常數、字元陣列、位元陣列、StringBuffer物件來建構字串物件。在J2SE 5.0之後,我們還可以適用新增的StringBuilder物件來建構物件。最簡單的建構字串物件的方式是直接傳入由「” ”」來產生一個只有含有空字串的字串物件,例如:

String str1 = new Srring(“這是一個字串”);

String str2 = new Srring(“”);            //建構一個空字串

◎傳入一個char陣列

String物件的建構子也可以接受其他型態的資料,例如:傳入char陣列的告方式為:

String 字串名稱 =  new String(char[] value);

以下的範例程式示範如何使用char陣列來初始話陣列:

char[] value = new char[] {‘’,’’,’J’,’a’,’v’,’a’};

String str1 = new String(value);

傳入一個char陣列,並指定元素的內容

使用char陣列來初始String物件的內容時,不一定要將char陣列中的所有元素都當作是String物件的初始內容,例如:傳入char陣列,並指定元素值範圍的宣告方式為:

String字串名稱=new String(char[] value,int offset,int count);

其中,「offset」用來指定char 陣列的開始位置的索引值,而「count」則是指定需要取用的元素個數。以下的範例程式示範如何使用char陣列來將String物件的內容初始為「Java」:

 

char[] value = new char[]{‘’,’’,’J’,’a’,’v’,’a’};

String str1 = new String(value,2,4);

◎傳入一個byte陣列

String物件的建構子也可以接受byte[]型態得資料,宣告方式為:

String 字串名稱 =  new String(byte[] value);

以下的範列程式示範如何使用byte陣列來初始化陣列:

byte[] value = “學習Java” .getByte();

String str1 = new String(value);

傳入一個StringBuffer物件

必要時,我們也可以使用「StringBuffer」物件來當作是String物件的初始內容,例如:傳入「StringBuffer」物件宣告方式為:

String 字串名稱 =  new String(StringBuffer buffer);

以下的範列程式示範如何使用StringBuffer物件來將String物件的內容初始為「JAVA:

StringBuffer buffer = new StringBuffer(“JAVA”);

String str3 = new String(buffer);

◎傳入一個StringBuilder物件

J2SE5.0所提供的StringBuilder類別也可以當作String物件的初始內容,例如:傳入「StringBuilder」物件宣告方式為:

String 字串名稱 =  new String(StringBuilder buffer);

以下的範列程式示範如何使用StringBuilder物件來將String物件的內容初始為「JAVA:

StringBuilder buffer = new StringBuilder (“JAVA”);

String str3 = new String(buffer);

下列的範例中示範不同的字串建構方式:

class BuildString

{

        public static void main(String[] args) {

                //使用字串常數建立字串

                String str1 = new String("第一個字串");

                System.out.println(str1);

 

                //使用字元陣列建立字串

                char[] c = {'', '' , '', '', ''};

                String str2 = new String(c);

                System.out.println(str2);

 

                //使用位元陣列建立一個字串

                byte[] b = "第三個字串".getBytes();

                String str3 = new String(b);

                System.out.println(str3);

 

                //使用StringBuffer建立一個字串

                StringBuffer sb = new StringBuffer("第四個字串");

                String str4 = new String(sb);

                System.out.println(str4);

 

                //使用StringBuilder建立一個字串

                StringBuilder sbr = new StringBuilder("第五個字串");

                String str5 = new String(sbr);

                System.out.println(str5);

 

                //將字串常數指定給字串物件

                String str6 = "第六個字串";

                System.out.println(str6);

        }

}

範例程式的執行結果:

第一個字串

第二個字串

第三個字串

第四個字串

第五個字串

第六個字串

         參考第29行的程式碼。該行中並沒有使用到new關鍵自來建構物件。這是因為字串是一種使用相當頻繁的資料型態,為了見話使用上的方便性,Java允許我們在程式中直接以字串常數來定義字串:

String str1 = “這是一組字串”;

String str2 = “這是一組字串”;

String str3 = new String (“這是一組字串”);

String str4 = new String (“這是一組字串”);

String str5 =” ”;        //建構一個空字串

 

        在上述的程式碼執行後,「str1」、「str2」、「str3」、「str4」都是含有「這是一組字串」內容的字串物件,但物件建構的方式卻是大異其趣。請參考下圖:

JavaCompiler在遇到「String str2 = “這是一組字串”;」的宣告方式時,會自動在所謂的「literal pool」中尋找是否有相同內容的字串常數,若有,則傳回該字串常數的地址:如果找不到,則會建立一個新的「String」物件,並初始該物件的內容,在傳回該物件的地址。

        使用「String str3 = new String (“這是一組字串”)」的方式來建立字串物件的效能會較差。因為在建立物件時,Compiler不會在「literal pool」中找尋是否有相同的字串常數,而是直接建立一個含有「這是一組字串」的字串物件。但程式在執行「new String(“這是一組字串”)」時,又會在產生一個具有「這是一組字串」的字串物件,在將該物件的地址傳回。實際上,在宣告的過程中Compiler會產生兩個「這是一組字串」的字串物件,只是其中的一個物件不會被參考到,而自動回收。

 

          以下的範例程式示範這兩種方式建立的字串物件的比較結果:

class String1 {

        public static void main(String[] args){

                String str1 = "這是一組字串";      //利用字串常數建立字串

                String str2 = "這是一組字串";      //再利用字串常數建立字串

                String str3 = new String("這是一組字串");

                String str4 = new String("這是一組字串");

                System.out.println("str1str2是否參考到同一物件:" + (str1 == str2));

                System.out.println("str1str3是否參考到同一物件:" + (str1 == str3));

                System.out.println("str3str4是否參考到同一物件:" + (str3 == str4));

                System.out.println("str1str2的內容是否相同:" + str1.equals(str2));

                System.out.println("str1str3的內容是否相同:" + str1.equals(str3));

                System.out.println("str3str4的內容是否相同:" + str3.equals(str4));

}

}

程式的執行結果為:

        範例程式的第3行利用字串常數建立了第一個字串物件,因為此時的「Iiteral pool」中並無「這是一個字串」的存在,所以會新建一個字串物件。程式的ㄉㄧ第4行又再次的建立一個字串物件,但因為初始內和「str1」的內容相同,所以Compiler會直接將已存在的物件的位址傳回,而不會新建一個字串物件。所以程式第9行的執行結果會是「true」。

        程式的第67行都是使用「new」來產生字串物件。這樣的建構方式會使Compiler直接產生新的字串物件。所以,程式的第1011行的比較結果會是「false」。請注意,此時記憶體中會有三個「這是一個字串」字串物件。

        程式中的第1314行利用equals方法來比較字串的內容是否相同,因為StringJava中的預設類別,所以您可以使用equals方法而得到正確的結果。這三行比較的結果都會是true

String物件的內容是不能更改的

       「String」物件一旦建立後,其內容就無法再「更改(immutable)」了。即使您將該String變數重新指定新的內容,Java的運作方式世新建立一個String物件,在將新的String物件指定給String變數。

        要了解這個觀念前,您需要先了解String的儲存方式。如果希望在儲存String物件後,並可以快速的再次找到該物件,最好的方法就是將這些物件存在「雜錯表(hash table)」中。Hash table使用特定的演算法算出物件的hash code,然後在利用hash codetable中找出該物件。物件都能被存在hash code中,不同的物件會有不同的hash Code。以String物件而言,我們可以利用「hashCode」找出String的儲存位置,而這個方法示覆寫自「Object」類別中的「hashCode」方法。參考以下的範例程式,您將會更了解這個概念:

class HashCode {

        public static void main(String[] args){

                String str = "Hello World";    //宣告String物件,並指定內容

                System.out.println("變更前,strhash Code為:" + str.hashCode());

 

                str = "Hello Java";          //現在更改str的內容

                System.out.println("變更後,strhash Code為:" + str.hashCode());

        }

}

程式執行結果為:

        您在執行範例程式時,所得到的數值不會和上述的執行結果相同,但「str」變更前後的hash Code是不會相同的。這告訴我們,我們所謂的變更String物件的內容其實是另一個新的String物件的產生。

String物件的常用方法

     由於String使用上得頻繁,您有必要自行瀏覽SDK文件中關於「java.lang」套件的String類別的相關方法,常用的方法有:

方法

公用

char charAt(int index)

取行字串中的index位置的字元

int compareTo(String anotherString)

依字母順序比較兩個字串是否完全相同

int cpmpareTolgnoreCase(String anotherString)

依字幕順序比較兩個字字串是否相同,比較時忽略字母的大小寫

String concate(String str)

將傳入的str字串連在呼叫該方法的字串後方

static String copyVauleOf(char[] data)

將字元陣列data的內容複製到呼叫該方法的字串中

boolean equals(Object anObject)

比較字串物件和anObject物件是否相同

byte[] getBytes()

將字串以位元陣列的型態傳回

int hashCode()

傳回字串的hashCode

int indexOf(int ch)

傳回ch字元在字傳的位置

Int length()

取得字串的長度

String replace(char oldChar,char newChar)

將字串的oldChar字元傳換成newChar字元

String substring(int beginIndex)

從字串中第beginIndex位置開始,取出子字串

String substring(int beginIndex,int endIndex)

取出字串中從beginInedx位置到endInedx位置的子字串

char[] toCharArray()

String物件的內容傳換為char陣列並傳回

String toString()

傳回String物件的本身的內容

String toLowerCase()

String物件中的英文字轉為小寫並傳回

 

String toUpperCase()

String物件中的英文字轉為大寫並傳回

String trim()

將字串兩端的空格移除

static String valueOf(Object obj)

obj可以是他的物件或是基本資料型態的變數,obj傳換為字串

◎字串屬性的取得及轉型

        我們可以使用「length」來取得字串的長度。必要時,可以使用「toCharArray」方法將字串轉換為字元陣列。匿用「getBytes」方法可以將字串轉換為位元陣列。如果要進行字元大小寫的轉換,我們可以使用「toLowerCase」或是「toUpperCase」等方法。各方法的使用請參考以下的範例:

class StringMethod1 {

        public static void main(String[] args){

                String str = "字串的操作ABc";    //宣告String物件,並指定內容

                int i;

                int slength = str.length();

                System.out.println("str字串的長度為:" + slength);

                char[] tmpChar = str.toCharArray();             //str轉換為char陣列

                System.out.print("tmpChar陣列的內容為:");

                for (i = 0; i < tmpChar.length; i++){

                        System.out.print(tmpChar[i]);

                }

                System.out.println();

                byte[] tmpByte = str.getBytes();            //str轉換為byte陣列

                System.out.print("tmpByter陣列的內容為:");

                for (i = 0; i < tmpByte.length; i++){

                        System.out.print(tmpByte[i] + " ");  //注意到每個中文字佔兩個byte

                }

                System.out.println();

 

                String tmpLower = str.toLowerCase();          //將字串中的英文字轉為小寫

                System.out.println("英文字轉為小寫後的內容:" + tmpLower);

 

String tmpUpper = str.toUpperCase();          //將字串中的英文字轉為大寫

                System.out.println("英文字轉為大寫後的內容:" + tmpUpper);

        }

}

程式的執行結果為:

◎字串內容的取得及搜尋

        利用「charAt」方法可以從字串中取出某個索引值位置的字元。「indexOf」方法可以用來在字串中搜尋特定的子字串。「indexOf()」和「lastIndexOf()」方法尚有其他的同名異式的函式,但皆是用於字串的搜尋,限於篇幅,您可以自行查閱SDK文件的相關說明。各方法的使用請參考以下的範例:

class StringMethod2 {

        public static void main(String[] args){

                String str = "字串的操作ABc,字串方法的使用";    //宣告String物件,並指定內容

                int i;

 

                System.out.println("str字串的內容是:" + str);

 

                i = 2;                                                       //設定index的值為2

                char find = str.charAt(i);                //找出第三個字

                System.out.println("str字串的第三個字為:" + find);

 

                String findStr = "字串";         //設定需要搜尋的字串內容

                i = str.indexOf(findStr);         //找尋「字串」的位置

                System.out.println("「字串」在str中的第一個出現的位置是:" + i);

                i = str.lastIndexOf(findStr);   //找尋「字串」最後出現的位置

                System.out.println("「字串」在str中的最後一個出現的位置是:" + i);

                findStr = "Java";                             //重新設定字串的內容

                i = str.indexOf(findStr);         //找尋「Java」的位置,這次找不到

                System.out.println("Java」在str中的第一個出現的位置是:" + i);

        }

}

程式的執行結果為:

◎字串內容的處理

        除了使用「+」運算子之外,我們可以使用「concate」方法來進行字串連接的動作。「replace」方法可以用來將字串中的某個字元取代成另一個字元。「substring」方法可以用來取出子字串。利用「split」方法,我們可以依照某個規則來分割字串

各方法的使用請參考以下的範例:

class StringMethod3 {

        public static void main(String[] args){

                String str = " 枯藤,老樹,昏鴉, ";        //宣告String物件,並指定內容

                String resultStr;

                System.out.println("str字串的內容是:" + str);

                resultStr = str.trim();                              //將原字串前後的空白刪除

                System.out.println("====將字串的前後空白清除=======");

                System.out.println(" 新字串的內容是:" + resultStr + "\n");

                String addStr = "小橋,流水,人家";

                System.out.println("====將原字串再連接新的字串=======");

                resultStr = resultStr.concat(addStr);      //再連接一組字串

                System.out.println("連接後,字串的內容是:" + resultStr + "\n");

                System.out.println("====取出原字串第4個字開始的內容=======");

                String tmpStr = resultStr.substring(3);          //取出第4個字()之後的所有內容

                System.out.println("取出後,新字串的內容是:" + tmpStr + "\n");

                System.out.println("====取出原字串第45個字的內容=======");

                tmpStr = resultStr.substring(3, 5);         //取出第4~5位置個字()之後的所有內容

                System.out.println("取出後,新字串的內容是:" + tmpStr + "\n");

                System.out.println("====將字串中的「,」取代為「。」=======");

                tmpStr = resultStr.replace("", "");

                System.out.println("取代後,新字串的內容是:" + tmpStr + "\n");

String[] splitStr;

                System.out.println("====在原字串中的「。」位置,分成字串陣列=======");

                splitStr = tmpStr.split("");

                int i;

                for(i = 0; i < splitStr.length; i++){

                        System.out.println(splitStr[i]);

                }

        }

}

程式執行的結果為:

String物件的內容真的不能更改

        林林總總的介紹這麼多的String類別的方法,您是否還記得String物件的內容是不能更改的重要特性?介紹過的String類別的方法中很多是回傳String的型態。但是,這是執行過程中「新建構」的String物件而不是原本的String物件。我們以「concate」這個方法來示範這個觀念。請參考以下的範例:

class StringMethod4 {

        public static void main(String[] args){

                String str = " Java ";       //宣告String物件,並指定內容

                System.out.println("相連前,str字串的內容是:" + str);

                String addStr = "Hello";

                System.out.println("addstr字串的內容是:" + addStr);

                String resultStr;

                resultStr = str.concat(addStr);                //將字串相連並傳回

                System.out.println("相連後,str字串的內容是:" + str);

                System.out.println("相連後,resultStr字串的內容是:" + resultStr);

        }

}

程式執行的結果為:

您從程式第8行的執行結果可以觀察到,str的內容一直都沒有變更過,而resultStr物件變數指向的是一個新產生的String物件。