這個謎題要問的是一個悅耳的問題,下面的程序將打印什么呢?
public class ABC{
public static void main(String[] args){
String letters = "ABC";
char[] numbers = {’1’, ’2’, ’3’};
System.out.println(letters + " easy as " + numbers);
}
}
可能大家希望這個程序打印出ABC easy as 123。遺憾的是,它沒有。如果你運行它,就會發(fā)現它打印的是諸如ABC easy as [C@16f0472之類的東西。為什么這個輸出會如此丑陋?
盡管char是一個整數類型,但是許多類庫都對其進行了特殊處理,因為char數值通常表示的是字符而不是整數。例如,將一個char數值傳遞給println方法會打印出一個Unicode字符而不是它的數字代碼。字符數組受到了相同的特殊處理:println的char[]重載版本會打印出數組所包含的所有字符,而String.valueOf和StringBuffer.append的char[]重載版本的行為也是類似的。
然而,字符串連接操作符在這些方法中沒有被定義。該操作符被定義為先對它的兩個操作數執(zhí)行字符串轉換,然后將產生的兩個字符串連接到一起。對包括數組在內的對象引用的字符串轉換定義如下[JLS 15.18.1.1]:
如果引用為null,它將被轉換成字符串"null"。否則,該轉換的執(zhí)行就像是不用任何參數調用該引用對象的toString方法一樣;但是如果調用toString方法的結果是null,那么就用字符串"null"來代替。
那么,在一個非空char數組上面調用toString方法會產生什么樣的行為呢?數組是從Object那里繼承的toString方法[JLS 10.7],規(guī)范中描述到:“返回一個字符串,它包含了該對象所屬類的名字,’@’符號,以及表示對象散列碼的一個無符號十六進制整數”[Java-API]。有關Class.getName的規(guī)范描述到:在char[]類型的類對象上調用該方法的結果為字符串"[C"。將它們連接到一起就形成了在我們的程序中打印出來的那個丑陋的字符串。
有兩種方法可以訂正這個程序。你可以在調用字符串連接操作之前,顯式地將一個數組轉換成一個字符串:
System.out.println(letters + " easy as " +
String.valueOf(numbers));
或者,你可以將System.out.println調用分解為兩個調用,以利用println的char[]重載版本:
System.out.print(letters + " easy as ");
System.out.println(numbers);
請注意,這些訂正只有在你調用了valueOf和println方法正確的重載版本的情況下,才能正常運行。換句話說,它們嚴格依賴于數組引用的編譯期類型。
下面的程序說明了這種依賴性??雌饋硭袷撬枋龅牡诙N訂正方式的具體實現,但是它產生的輸出卻與最初的程序所產生的輸出一樣丑陋,因為它調用的是println的Object重載版本,而不是char[]重載版本。
class ABC2{
public static void main(String[] args){
String letters = "ABC";
Object numbers = new char[] { ’1’, ’2’, ’3’ };
System.out.print(letters + " easy as ");
System.out.println(numbers);
}
}
總之,char數組不是字符串。要想將一個char數組轉換成一個字符串,就要調用String.valueOf(char[])方法。某些類庫中的方法提供了對char數組的類似字符串的支持,通常是提供一個Object版本的重載方法和一個char[]版本的重載方法,而之后后者才能產生我們想要的行為。
對語言設計者的教訓是:char[]類型可能應該覆寫toString方法,使其返回數組中包含的字符。更一般地講,數組類型可能都應該覆寫toString方法,使其返回數組內容的一個字符串表示。
public class ABC{
public static void main(String[] args){
String letters = "ABC";
char[] numbers = {’1’, ’2’, ’3’};
System.out.println(letters + " easy as " + numbers);
}
}
可能大家希望這個程序打印出ABC easy as 123。遺憾的是,它沒有。如果你運行它,就會發(fā)現它打印的是諸如ABC easy as [C@16f0472之類的東西。為什么這個輸出會如此丑陋?
盡管char是一個整數類型,但是許多類庫都對其進行了特殊處理,因為char數值通常表示的是字符而不是整數。例如,將一個char數值傳遞給println方法會打印出一個Unicode字符而不是它的數字代碼。字符數組受到了相同的特殊處理:println的char[]重載版本會打印出數組所包含的所有字符,而String.valueOf和StringBuffer.append的char[]重載版本的行為也是類似的。
然而,字符串連接操作符在這些方法中沒有被定義。該操作符被定義為先對它的兩個操作數執(zhí)行字符串轉換,然后將產生的兩個字符串連接到一起。對包括數組在內的對象引用的字符串轉換定義如下[JLS 15.18.1.1]:
如果引用為null,它將被轉換成字符串"null"。否則,該轉換的執(zhí)行就像是不用任何參數調用該引用對象的toString方法一樣;但是如果調用toString方法的結果是null,那么就用字符串"null"來代替。
那么,在一個非空char數組上面調用toString方法會產生什么樣的行為呢?數組是從Object那里繼承的toString方法[JLS 10.7],規(guī)范中描述到:“返回一個字符串,它包含了該對象所屬類的名字,’@’符號,以及表示對象散列碼的一個無符號十六進制整數”[Java-API]。有關Class.getName的規(guī)范描述到:在char[]類型的類對象上調用該方法的結果為字符串"[C"。將它們連接到一起就形成了在我們的程序中打印出來的那個丑陋的字符串。
有兩種方法可以訂正這個程序。你可以在調用字符串連接操作之前,顯式地將一個數組轉換成一個字符串:
System.out.println(letters + " easy as " +
String.valueOf(numbers));
或者,你可以將System.out.println調用分解為兩個調用,以利用println的char[]重載版本:
System.out.print(letters + " easy as ");
System.out.println(numbers);
請注意,這些訂正只有在你調用了valueOf和println方法正確的重載版本的情況下,才能正常運行。換句話說,它們嚴格依賴于數組引用的編譯期類型。
下面的程序說明了這種依賴性??雌饋硭袷撬枋龅牡诙N訂正方式的具體實現,但是它產生的輸出卻與最初的程序所產生的輸出一樣丑陋,因為它調用的是println的Object重載版本,而不是char[]重載版本。
class ABC2{
public static void main(String[] args){
String letters = "ABC";
Object numbers = new char[] { ’1’, ’2’, ’3’ };
System.out.print(letters + " easy as ");
System.out.println(numbers);
}
}
總之,char數組不是字符串。要想將一個char數組轉換成一個字符串,就要調用String.valueOf(char[])方法。某些類庫中的方法提供了對char數組的類似字符串的支持,通常是提供一個Object版本的重載方法和一個char[]版本的重載方法,而之后后者才能產生我們想要的行為。
對語言設計者的教訓是:char[]類型可能應該覆寫toString方法,使其返回數組中包含的字符。更一般地講,數組類型可能都應該覆寫toString方法,使其返回數組內容的一個字符串表示。