謎題11:最後的笑聲
下面的程序將打印出什麽呢?
public class LastLaugh{
public static void main(String[] args){
System.out.print("H"+"a");
System.out.print(‘H’+‘a’);
}
}
你可能會認為這個程序將打印HaHa。該程序看起來好像是用兩種方式連接了H和a,但是你所見為虛。如果你運行這個程序,就會發現它打印的是Ha169。那麽,為什麽它會產生這樣的行為呢?
正如我們所期望的,第一個對System.out.print的調用打印的是Ha:它的參數是表達式"H"+"a",顯然它執行的是一個字符串連接。而第二個對System.out.print的調用就是另外一回事了。問題在於‘H’和‘a’是字符型字面常量,因為這兩個操作數都不是字符串類型的,所以 + 操作符執行的是加法而不是字符串連接。
編譯器在計算常量表達式‘H’+‘a’時,是通過我們熟知的拓寬原始類型轉換將兩個具有字符型數值的操作數(‘H’和‘a’)提升為int數值而實現的。從char到int的拓寬原始類型轉換是將16位的char數值零擴展到32位的int。對於‘H’,char數值是72,而對於‘a’,char數值是97,因此表達式‘H’+‘a’等價於int常量72 + 97,或169。
站在語言的立場上,若幹個char和字符串的相似之處是虛幻的。語言所關心的是,char是一個無符號16位原始類型整數——僅此而已。對類庫來說就不盡如此了,類庫包含了許多可以接受char參數,並將其作為Unicode字符處理的方法。
那麽你應該怎樣將字符連接在一起呢?你可以使用這些類庫。例如,你可以使用一個字符串緩沖區:
StringBuffer sb = new StringBuffer();
sb.append(‘H’);
sb.append(‘a’);
System.out.println(sb);
這麽做可以正常運行,但是顯得很醜陋。其實我們還是有辦法去避免這種方式所產生的拖沓冗長的代碼。 你可以通過確保至少有一個操作數為字符串類型,來強制 + 操作符去執行一個字符串連接操作,而不是一個加法操作。這種常見的慣用法用一個空字符串("")作為一個連接序列的開始,如下所示:
System.out.println("" + ‘H’ + ‘a’);
這種慣用法可以確保子表達式都被轉型為字符串。盡管這很有用,但是多少有一點難看,而且它自身可能會引發某些混淆。你能猜到下面的語句將會打印出什麽嗎?如果你不能確定,那麽就試一下:
System.out.print("2 + 2 = " + 2+2);
如果使用的是JDK 5.0,你還可以使用
System.out.printf("%c%c", ‘H’, ‘a’);
總之,使用字符串連接操作符使用格外小心。+ 操作符當且僅當它的操作數中至少有一個是String類型時,才會執行字符串連接操作;否則,它執行的就是加法。如果要連接的沒有一個數值是字符串類型的,那麽你可以有幾種選擇:
1,預置一個空字符串;
2,將第一個數值用String.valueOf顯式地轉換成一個字符串;
3,使用一個字符串緩沖區;
4,或者如果你使用的JDK 5.0,可以用printf方法。
這個謎題還包含了一個給語言設計者的教訓。操作符重載,即使在Java中只在有限的範圍內得到了支持,它仍然會引起混淆。為字符串連接而重載 + 操作符可能就是一個已鑄成的錯誤。
謎題11:最後的笑聲