大頭兒子和小頭爸爸的戰鬥–java字符和字符串
- 2019 年 10 月 11 日
- 筆記
故事背景
一座普普通通的小屋裡,住着大頭兒子、小頭爸爸和圍裙媽媽。在他們普普通通的生活中,總是響起充滿歡樂的笑聲。最溫暖的家又成了他們每個人的愛的源泉。
《大頭兒子和小頭爸爸》是孩子居首(大頭),媽媽居中,爸爸最末(小頭);爸爸主外,媽媽主內(圍裙),他們是中國現代家庭教育典型的縮影。
java中的大頭兒子和小頭爸爸
java中也有一對冤家對頭,他們就是字符char和字符串String。來看看他們的表現吧:
System.out.println("h"+"i"); System.out.println('h'+'i');
會打印出什麼呢?結果可能出乎你的意外:
hi
209
為什麼會出現209這個結果呢?
編譯器在計算常量表達式’h’+’i’時,是通過我們熟知的拓寬原始類型轉換將兩個具有字符型數值的操作數(’h’和’i’)提升為int 數值而實現的。從char 到int 的拓寬原始類型轉換是將16 位的char 數值零擴展到32 位的int。對於’h’,char 數值是104,而對於’i’,char 數值是105,因此表達式’h’+’i’等價於int常量104 + 105,或209。
有三種方式避免出現char的連接問題。第一種最簡單:
System.out.println("" + 'h' + 'i');
第二種:使用函數
System.out.printf("%c%c", 'h', 'i'); 或者 System.out.println(String.format("%c%c", 'h','i'));
第三種,利用api拼裝:
StringBuffer sb = new StringBuffer(); sb.append('h'); sb.append('i'); System.out.println(sb);
也會你會認為這比較簡單,那我們就見識一個比較複雜點的吧!
private static Random rnd = new Random(); public static void main(String[] args) { StringBuffer word = null; switch(rnd.nextInt(2)) { case 1: word = new StringBuffer('P'); case 2: word = new StringBuffer('G'); default: word = new StringBuffer('M'); } word.append('a'); word.append('i'); word.append('n'); System.out.println(word); }
乍一看,這個程序可能會在一次又一次的運行中,以相等的概率打印出Pain,Gain 或 Main。看起來該程序會根據隨機數生成器所選取的值來選擇單詞的第一個字母:0 選M,1 選P,2 選G。它實際上既不會打印Pain,也不會打印Gain。也許更令人吃驚的是,它也不會打印Main,並且它的行為不會在一次又一次的運行中發生變化,它總是在打印ain。這又是為什麼呢?
多個問題,糾結在一起導致了這個問題:
1.Random.nextInt(int)的規範描述道:“返回一個偽隨機的、均等地分佈在從0(包括)到指定的數值(不包括)之間的一個int 數值”[Java-API]。這意味着表達式rnd.nextInt(2)可能的取值只有0和1,Switch語句將永遠也到不了case2 分支,這表示程序將永遠不會打印Gain。nextInt 的參數應該是3 而不是2。
2. case中沒有任何break 語句。不論switch 表達
式為何值,該程序都將執行其相對應的case 以及所有後續的case[JLS 14.11]。因此,儘管每一個case 都對變量word 賦了一個值,但是總是最後一個賦值勝出,覆蓋了前面的賦值。最後一個賦值將總是最後一種情況(default),即new StringBuffer{‘M’}。這表明該程序將總是打印Main,而從來不打印Pain或Gain。
3.在本例中,編譯器會選擇接受int 的構造器,通過拓寬原始類型轉換把字符數值’M’轉換為一個int 數值77[JLS 5.1.2]。換句話說,new StringBuffer(‘M’)返回的是一個具有初始容量77 的空的字符串緩衝區。該程序餘下的部分將字符a、i 和n 添加到了這個空字符串緩衝區中,並打印出該字符串緩衝區那總是ain 的內容。
修改如下:
private static Random rnd = new Random(); public static void main(String[] args) { System.out.println("PGM".charAt(rnd.nextInt(3)) + "ain"); }
參考資料
【1】java解惑
【2】https://baike.baidu.com/item/%E5%A4%A7%E5%A4%B4%E5%84%BF%E5%AD%90%E5%92%8C%E5%B0%8F%E5%A4%B4%E7%88%B8%E7%88%B8/2346537?fromtitle=%E5%A4%A7%E5%A4%B4%E5%84%BF%E5%AD%90%E5%B0%8F%E5%A4%B4%E7%88%B8%E7%88%B8&fromid=3076860&fr=aladdin