Java SE提供了標準API,這些API就是由許多類別所組成,你可以直接取用這些標準類別,省去你撰寫程式時重新打造輪子的需求。底下來舉兩個基本的標準類別:java.util.Scanner
與java.math.BigDecimal
。
使用java.util.Scanner
舉例來說,目前為止的程式範例都很無聊,變數值都是寫死的,沒有辦法接受使用者的輸入。如果要在文字模式下取得使用者輸入,基本上可以使用System.in
物件上的read()
方法,不過這個方法是以int
型態傳回讀入的字元編碼。想想,如果你輸入了一個'9'
字元,使用System.in.read()
的話,就還得自己將'9'
字元轉換為整數9,當然是不方便,實際上,System.in.read()
這類的方法,是作為底層API使用,開發者會在上頭依需求施予更高層次的封裝。
java.util.Scanner
就是這類的封裝,底下直接以實際範例來說明:
package cc.openhome;
import java.util.Scanner;
public class Guess {
public static void main(String[] args) {
Scanner userInput = new Scanner(System.in);
int number = (int) (Math.random() * 10);
int guess;
do {
System.out.print("猜數字(0 ~ 9):");
guess = userInput.nextInt();
} while(guess != number);
System.out.println("猜中了...XD");
}
}
由於你不想每次都鍵入java.util.Scanner
,所以一開始就使用import
告訴編譯器,如此之後就只要鍵入Scanner
就可以了。在建立Scanner
實例時,必須傳入java.io.InputStream
的實例,之後介紹到輸入輸出串流時會知道,System.in
就是一種InputStream
,所以在這邊可以在建構Scanner
實例時使用。
接下來你想要什麼資料,就跟Scanner
物件要就可以了,正如其名,Scanner
實例會幫你掃描標準輸入,看看使用者有無輸入字元,怎麼掃描你就不用管了,反正一定是有個Scanner.java定義了程式碼作這些事,編譯為Scanner.class並放在rt.jar中供大家使用。
Scanner
的nextInt()
方法會看看標準輸入中,有沒有輸入下一個字串(以空白或換行為區隔),有的話會嘗試將之剖析為int
型態,Scanner
對每個基本型態,都會有個對應的nextXXX()
方法,例如nextByte()、nextShort()、nextLong()、nextFloat()、nextDouble()、nextBoolean()
等,如果直接取得上一個字串(以空白或換行為區隔),則使用next()
,如果想取得使用者輸入的整行文字,則使用nextLine()
(以換行為區隔)。
慣例上,套件名稱為java
開頭的類別,表示標準類別。
使用java.math.BigDecimal
知道在 Java 中執行1.0 – 0.8
的結果是多少嗎?答案不是0.2,而是0.19999999999999996!為什麼?這是Java的臭蟲(Bug)嗎?不!不是的!你使用別的程式語言(例如JavaScript、Python等)也有可能是顯示這個結果。
簡單來說,Java(包括其它程式語言)遵合IEEE 754浮點數演算(Floating-point arithmetic)規範,使用分數與指數來表示浮點數。例如0.5會使用1/2來表示,0.75會使用1/2 + 1/4來表示,0.875會使用1/2 + 1/4 + 1/8來表示,而0.1會使用1/16 + 1/32 + 1/256 + 1/512 +1/4096 + 1/8192 + ...無限循環下去,無法精確表示,因而造成運算上的誤差。
再來舉個例子,你覺得以下程式片段會顯示什麼結果?
double a = 0.1;
double b = 0.1;
double c = 0.1;
if((a + b + c) == 0.3) {
System.out.println("等於 0.3");
}
else {
System.out.println("不等於 0.3");
}
由於浮點數誤差的關係,結果是顯示「不等於0.3」!類似的例子還很多,結論就是,如果要求精確度,那就要小心使用浮點數,而且別用==
直接比較浮點數運算結果。
那麼要怎麼辦得到更好的精確度?可以使用java.math.BigDecima
l類別,以方才的1.0 – 0.8
為例,如何得到0.2的結果?直接使用程式來示範:
package cc.openhome;
import java.math.BigDecimal;
public class DecimalDemo {
public static void main(String[] args) {
BigDecimal operand1 = new BigDecimal("1.0");
BigDecimal operand2 = new BigDecimal("0.8");
BigDecimal result = operand1.subtract(operand2);
System.out.println(result);
}
}
建構BigDecimal
的方法之一是使用字串,BigDecmial
在建構時會剖析傳入字串,以預設精度進行接下來的運算,BigDecimal
提供有plus()、substract()、multiply()、divide()
等方法,可以進行加、減、乘、除等運算,這些方法都會傳回代表運算結果的BigDecimal
。
上面這個範例可以顯示出0.2的結果,再來看利用BigDecimal
比較相等的例子:
package cc.openhome;
import java.math.BigDecimal;
public class DecimalDemo2 {
public static void main(String[] args) {
BigDecimal op1 = new BigDecimal("0.1");
BigDecimal op2 = new BigDecimal("0.1");
BigDecimal op3 = new BigDecimal("0.1");
BigDecimal result = new BigDecimal("0.3");
if(op1.add(op2).add(op3).equals(result)) {
System.out.println("等於 0.3");
}
else {
System.out.println("不等於 0.3");
}
}
}
由於BigDecimal
的add()
等方法都會傳回代表運算結果的BigDecmial
,所以就直接利用傳回的BigDecimal
再呼叫add()
方法,最後再呼叫equals()
比較兩個BigDecimal
實質上是否相同,所以有了op1.add(op2).add(op3).equals(result)
的寫法。
你可以在 JWorld@TW 的 [FAQ] 為何 1.0 - 0.8 不是 0.2? 討論中,看到更多浮點數誤差的例子。