Java SE 6 技術手冊

3.2 在文字模式下與程式互動

從互動中學習,是我最喜愛的學習方式,我建議學習 Java 的第一步,要先能看到您的程式執行結果,要可以對程式輸入一些資料,作一些傻瓜式的互動。

3.2.1 使用 Scanner 取得輸入

在文字模式下要輸入資料至程式中時,您可以使用標準輸入串流物件 System.in,然而實際上很少直接使用它,因為 System.in 物件所提供的 read() 方法,是從輸入串流取得一個位元組的資料,並傳回該位元組的整數值,但通常您要取得的使用者輸入會是一個字串,或是一組數字,所以 System.in 物件的 read() 方法一次只讀入一個位元組資料的方式並不適用。

在 J2SE 5.0 中,您可以使用 java.util.Scanner 類別取得使用者的輸入,java.util 指的是套件(package)層級,java.util.Scanner 表示 Scanner 這個類別是位於 java/util 這樣的階層之下,現階段您可以將這個階層想像為類似檔案管理的目錄階層。 直接使用實例來看看如何使用 Scanner 取得使用者的輸入字串:

範例 3.5 ScannerDemo.java

import java.util.Scanner;

public class ScannerDemo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("請輸入您的名字:");
        System.out.printf("哈囉! %s!\n", scanner.next());
    }
}

先看看執行結果再來解釋程式的內容:

請輸入您的名字:良葛格
哈囉! 良葛格!

java.util 套件是 Java SE 的標準套件,您使用 import 是在告訴編譯器,您將使用 java.util下 的 Scanner 類別。

new 關鍵字表示您要新增一個 Scanner 物件,在新增一個 Scanner 物件時需要一個 System.in 物件,因為實際上還是 System.in 在取得使用者的輸入,您可以將 Scanner 看作是 System.in 物件的支援者,System.in 取得使用者輸入之後,交給 Scanner 作一些處理(實際上,這是 Decorator 模式的一個應用,請見這一章後的索引)。

不以 Java 術語而以白話來說,您告訴執行環境給您一個叫作 Scanner 的工具,然後您可以使用這個工具的 next() 功能,來取得使用者的輸入字串,但要如何取得數字呢?您可以使用 Scanner 工具的 nextInt() 功能,例如:

範例 3.6 ScannerDemo2.java

import java.util.Scanner;

public class ScannerDemo2 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("請輸入一個數字: ");
        System.out.printf("您輸入了 %d !\n", 
                           scanner.nextInt());
    }
}

nextInt() 會將試著取得的字串轉換為 int 型態的整數,關於資料型態(Data type)我待會就會介紹,先來看看執行結果:

請輸入一個數字: 100
您輸入了 100 !

依同樣的方式,您還可以使用 Scanner 的 nextFloat()、nextBoolean() 等方法來取得使用者的輸入,並轉換為正確的資料型態。

在 JDK6 之中,您還可以使用 System.console() 方法取得一個 java.io.Console 物件,利用它來取得使用者輸入非常的方便,關於 Console 的介紹,請見第 21 章關於 JDK6 新功能的介紹。

3.2.2 使用 BufferedReader 取得輸入

Scanner 取得輸入的依據是空白字元,舉凡按下空白鍵、tab 鍵或是 enter 鍵,Scanner 就會傳回下一個輸入,所以在某些時候並不適用,因為使用者可能輸入一個字串,中間會包括空白字元,而您希望取得完整的字串,如果您想要取得包括空白字元的輸入,比較簡單的方法是使用 java.io.BufferedReader 類別取得輸入,這個方法也是在 Java SE 1.4 或之前版本下取得使用者輸入的方式。

必須先說明的是,關於使用 BufferedReader 來取得輸入,在理解上要複雜的多,我在這邊先加以說明,但如果您現階段無法理解的話不用氣餒,因為使用方法是固定的,每次使用前先如法泡製就可以了,在第14章介紹完輸入輸出,您就會理解 BufferedReader 的運作方式。

BufferedReader 類別是 java.io 套件中所提供的一個類別,所以使用這個類別時必須使用 import 告訴編譯器這個類別位於 java.io 套件下。

使用 BufferedReader 物件的 readLine() 方法必須處理 java.io.IOException 例外(exception),例外處理機制是 Java 提供給程式設計人員捕捉程式中可能發生的錯誤所提供的機制,現階段您處理 IOException 的方法是在 main() 方法加上 throws IOException,這在第 10 章介紹例外處理時,我會再詳細介紹為何要這麼作。

BufferedReader 在建構時接受一個 java.io.Reader 物件,在讀取標準輸入串流時,您可以使用 java.io.InputStreamReader,它繼承(Inherit)了 Reader類別,您可以使用以下的方法來為標準輸入串流建立緩衝區物件:

BufferedReader bufferedReader = new BufferedReader(
                               new InputStreamReader(System.in));

BufferedReader bufferedReader 表示宣告一個型態為 BufferedReader 的參考名稱,而 new BufferedReader() 表示您以 BufferedReader 類別建構一個物件,new InputStreamReader(System.in) 表示接受一個 System.in 物件來建構一個 InputStreamReader 物件。

不用Java術語而用白話來解釋上一段的話,就是您增加一個 BufferedReader 工具,這個工具中還要加上一個 InputStreamReader 工具,而 InputStreamReader 工具中實際的核心是 System.in 工具,這三個工具組合在一起,就可以讓您進行文字輸入的讀取。

如果還是不理解的話沒關係,照著抄寫就可以了,實際看個例子瞭解如何使用 BufferedReader 來讀取輸入。

範例 3.7 BufferedReaderDemo.java

import java.io.*; 

public class BufferedReaderDemo { 
    public static void main(String[] args) throws IOException { 
        BufferedReader bufferedReader = 
                          new BufferedReader(
                             new InputStreamReader(System.in)); 

        System.out.print("請輸入一列文字,可包括空白: "); 
        String text = bufferedReader.readLine(); 
        System.out.println("您輸入的文字: " + text); 
    } 
}

由於這次您所使用到的 BufferedReader、InputStreamReader 與 IOException 等類別,都是位在 java.io 套件下,所以在程式的一開頭可以使用 import 與 * 號,告訴編譯器到 java.io 下找這些類別。

readLine() 方法會傳回使用者在按下 Enter 鍵之前的所有字元輸入,不包括最後按下的 Enter 返回字元,程式的執行範例如下所示:

請輸入一列文字,可包括空白: Have a nice day :)
您輸入的文字: Have a nice day :)

良葛格的話匣子 學習一個新的事物時,如果遇到一些觀念無法馬上理解,這可能是因為要理解觀念會需要其它觀念先建立起來,所以先暫時放下這個疑問也是一個學習方法,稱之為「闕疑」,在往後的學習過程中待必要的觀念學會後,目前的疑問自然也會解開。

但什麼該闕疑?什麼觀念又該先徹底理解?在這本書中我會根據經驗告訴您,在告訴您闕疑的同時,也會告訴您這些觀念實際上在哪個章節(或網路資料上)會詳細說明。

3.2.3 標準輸入輸出串流

在之前的範例程式中,您使用了 System 類別中的靜態物件 out,它提供標準輸出串流(Stream)輸出,會在程式開始執行之後自動開啟並準備接受指定的資料,它通常對應至顯示輸出(文字模式、終端機輸出),您可以將輸出重新導向至一個檔案,只要執行程式時使用'>'將輸出結果導向至指定的檔案,例如:

java HelloJava > HelloJavaResult.txt

上面的執行會將原本文字模式下的顯示結果導向至 HelloJavaResult.txt,而不會在螢幕上顯示訊息,HelloJavaResult.txt 將會有執行的結果文字" 嗨!我的第一個Java程式!"。

除了標準輸出串流 out 之外,Java 程式在執行之後,還會開啟標準輸入串流 in 與標準錯誤輸出串流 err。標準輸入串流 in 也是 System 類別所提供的靜態物件,在程式開始之後它會自動開啟,對應至鍵盤或其它的輸入來源,準備接受使用者或其它來源的輸入,您可以使用 read() 方法來讀取輸入,不過通常很少直接使用,而會使用一個 Scanner 物件或 BufferedReader 來讀取輸入,方法我已經在前面的內容介紹過了。

標準錯誤輸出串流 err 也是在程式執行後自動開啟,它會將指定的字串輸出至顯示裝置或其它指定的裝置,與標準輸出串流 out 不同的是,err 會立即顯示指定的(錯誤)訊息給使用者知道,即使您指定程式將結果重新導向至檔案,err 輸出串流的訊息也不會被重新導向,而仍會顯示在指定的顯示裝置上,下面這個例子給您一個簡單的測試方式:

範例 3.8 ErrDemo.java

public class ErrDemo { 
    public static void main(String[] args) { 
         System.out.println("使用out輸出訊息"); 
         System.err.println("使用err輸出訊息"); 
    } 
}

在編譯程式之後,請如下執行程式,您會發現輸出結果如下:

java ErrDemo > ErrDemoResult.txt
使用err輸出訊息

開啟 ErrDemoResult.txt 之後,您會發現當中只有 "使用out輸出訊息" 的文字,而"使用 err 輸出訊息"的文字並沒有被導向至檔案中,而是直接顯示在文字模式中。

要重新導向輸出至指定的目的檔案是用 '>',也可以使用 '>>',後者除了重導標準輸出之外,還有附加(Append)的功能,也就是會把輸出的內容附加到目的檔案的後頭,如果目的檔案本來不存在就會先建立一個再進行輸出。

3.2.4 輸出格式控制

標準輸出通常是使用文字模式作為輸出,這邊我介紹幾個輸出格式控制技巧,在文字模式顯示時可以協助控制輸出的格式,首先介紹格式控制字元,先使用表格列出一些常用的控制字元:

表 3.1 常用格式控制字元

控制字元 作用
\\ 反斜線
\' 單引號'
\" 雙引號"
\uxxxx 以 16 進位數指定 Unicode 字元輸出
\xxx 以 8 進位數指定 Unicode 字元輸出
\b 倒退一個字元
\f 換頁
\n 換行
\r 游標移至行首
\t 跳格(一個Tab鍵)

範例 3.9 告訴您如何指定 Unicode 字元編碼來輸出 "Hello" 這段文字。

範例 3.9 OutputUnicode.java

public class OutputUnicode {
    public static void main(String[] args) {
        System.out.println("\u0048\u0065\u006C\u006C\u006F");
    }
}

在輸出數值時,預設都會以十進位的方式來顯示數值,範例 3.10 告訴您如何使用 java.lang.Integer 所提供的各種 toXXX() 方法來顯示不同進位制之數值。

範例 3.10 NumberDemo.java

public class NumberDemo {
    public static void main(String[] args) {
        // 十進位 19 轉成二進位 10011
        System.out.println(Integer.toBinaryString(19));

        // 十進位 19 轉成十六進位 13
        System.out.println(Integer.toHexString(19));

        // 十進位 19 轉成八進位 23
        System.out.println(Integer.toOctalString(19));
    }
}
`

若是使用 J2SE 5.0 或更高的版本,您可以使用 System.out.printf() 作簡單的輸出格式設定,例如:

範例 3.11 TigerNumberDemo.java

public class TigerNumberDemo {
    public static void main(String[] args) {
        // 輸出 19 的十進位表示
        System.out.printf("%d%n", 19);

        // 輸出 19 的八進位表示
        System.out.printf("%o%n", 19);

        // 輸出 19 的十六進位表示
        System.out.printf("%x%n", 19); 
    }
}

'%d' 表示將指定的數值以十進位表示,'%o' 是八進位表示,而 '%x' 是十六進位表示,'%n' 是指輸出平台特定的換行字元,如果是在 Windows 下實際上會置換為 'r\n',如果是 Linux 下則會置換為 '\n'。

表3.2 簡單列出了一些常用的轉換符號。

表 3.2 常用格式轉換字元

格式字元 作用
%% 在字串中顯示 %
%d 以 10 進位整數方式輸出,提供的數必須是 Byte、Short、 Integer、Long 或 BigInteger
%f 將浮點數以 10 進位方式輸出,提供的數必須是 Float、Double 或 BigDecimal
%e, %E 將浮點數以 10 進位方式輸出,並使用科學記號,提供的數必須是 Float、 Double 或 BigDecimal
%a, %A 使用科學記號輸出浮點數,以 16 進位輸出整數部份,以 10 進位輸出指數部份,提供的數必須是 Float、Double、BigDecimal
%o 以 8 進位整數方式輸出,提供的數必須是 Byte、Short、 Integer、Long 或 BigInteger
%x, %X 將浮點數以 16 進位方式輸出,提供的數必須是 Byte、Short、 Integer、Long 或 BigInteger
%s, %S 將字串格式化輸出
%c, %C 以字元方式輸出,提供的數必須是 Byte、Short、Character 或 Integer
%b, %B 將 "true" 或 "false" 輸出(或 "TRUE"、"FALSE",使用 %B)。另外,非 null 值輸出是 "true",null 值輸出是 "false"
%t, %T 輸出日期/時間的前置,詳請看線上 API 文件

您可以在輸出浮點數時指定精度,例如以下這行的執行結果會輸出 " example:19.23":

System.out.printf("example:%.2f%n", 19.234);

您也可以指定輸出時,至少要預留的字元寬度,例如:

System.out.printf("example:%6.2f%n", 19.234);

由於預留了 6 個字元寬度,不足的部份要由空白字元補上,所以執行結果會輸出如下(19.23 只佔五個字元,所以補上一個空白在前端):

example: 19.23

以上只是簡短的列出一些常用的輸出轉換符號,事實上,這些功能都是由 java.util.Formatter 所提供的,如果您需要更多關於輸出格式的控制,您可以看看線上 API 文件以查詢相關設定。

良葛格的話匣子 由於書的篇幅有限,我只在必要的時候列出一些常用的 API 文件表格說明,如果需要更多的說明,請直接查詢我所提供的文件索引,學會查詢線上文件也是學好 Java 的必備功夫喔!