2016/5/17

Java:TryCatch

TryCatch語法結構是這樣:
try{
    ...
}
catch(物件型態 ex){
    ...
}
簡單說,當try區塊裡的程式碼執行錯誤時,就執行例外處理catch裡的程式碼,catch會有一個參數,是Java把錯誤包裝成錯誤的物件型態。

例1:
class Try1Demo {
    public static void main(String[] args) {
        try {
            System.out.println(1 / 0);
        }
        catch (Exception ex) {
            System.out.println("something wrong");
        }
    }
}
1/0是個致命錯誤,但我們用TryCatch去處理,當1/0錯誤時,就執行catch的程式碼,會顯示something wrong。 Exception錯誤物件的形態,因:
除以 0 的致命錯誤屬於 ArimethicException 類別,而 ArimethicException 繼承自 RuntimeException , RuntimeException 又繼承自 Exception ,事實上,所有例外均繼承自 Exception ,因此使用 Exception 可抓住所有例外。
 所以catch裡的參數可放不同的錯誤物件型態,這裡也可放ArithmeticException型態的物件,也可這樣寫,兩個catch:
class Try3Demo {
    public static void main(String[] args) {
        try {
            System.out.println(1 / 0);
        }
        catch (ArithmeticException ex) {
            System.out.println("算術錯誤的例外");
        }
        catch (Exception ex) {
            System.out.println("something wrong");
        }
    }
}
那我們怎麼知道錯誤的時候,他是什麼型態?
試著把例1的的TryCatch拿掉,執行後會顯示
Exception in thread "main" java.lang.ArithmeticException: / by zero
at trycatch.TryCatch.main(
TryCatch.java:25)
C:\Users\...\AppData\Local\NetBeans\Cache\8.1\executor-snippets\run.xml:53: Java returned: 1
這樣,通常可以知道是什麼型態的物件。

例2,一個計算平均的程式,輸入0就停止並運算:
package trycatch;
import java.util.Scanner;
public class TryCatch {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        double sum = 0;
        int count = 0;
        int number;
        while(true) {
            number = scanner.nextInt();
            if(number == 0) {
                break;
            }
            sum += number;
            count++;
        }
        System.out.printf("平均 %.2f%n", sum / count);
    }
}
run結果:
d5
Exception in thread "main" java.util.InputMismatchException
 at java.util.Scanner.throwFor(Scanner.java:864)
 at java.util.Scanner.next(Scanner.java:1485)
 at java.util.Scanner.nextInt(Scanner.java:2117)
 at java.util.Scanner.nextInt(Scanner.java:2076)
 at trycatch.TryCatch.main(TryCatch.java:10)
C:\Users\...\AppData\Local\NetBeans\Cache\8.1\executor-snippets\run.xml:53: Java returned: 1
BUILD FAILED (total time: 10 seconds)
輸入非數字,程式會錯誤。
這時可以根據系統吐出的錯誤訊息,來做例外處理,可以看到第22行,InputMismatchException就是錯誤的物件,因此可更改程式為:
package trycatch;
import java.util.InputMismatchException;
import java.util.Scanner;
public class TryCatch {
    public static void main(String[] args) {
        try {
            Scanner scanner = new Scanner(System.in);
            double sum = 0;
            int count = 0;
            int number;
            while(true) {
                number = scanner.nextInt();
                if(number == 0) {
                    break;
                }
                sum += number;
                count++;
            }
            System.out.printf("平均 %.2f%n", sum / count);
        }
        catch(InputMismatchException ex){
            System.out.println("必須輸入整數");
        }
    }
}
這樣就可以應對輸入非數字的處理了。

例3,有時如果你這樣寫,竟然無緣無故錯誤:
public static void main(String[] args) {
        int ch = System.in.read();
        System.out.println(ch);
}
run:
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - unreported exception java.io.IOException; must be caught or declared to be thrown
 at trycatch.TryCatch.main(TryCatch.java:5)
C:\Users\...\AppData\Local\NetBeans\Cache\8.1\executor-snippets\run.xml:53: Java returned: 1
有兩種方法解決,一、用try、catch包裹System.in.read(),二、在main()方法旁宣告throws java.io.IOException。
像下面這樣:
try {
    int ch = System.in.read();
    System.out.println(ch);
}
catch(java.io.IOException ex) {
    ex.printStackTrace() ;
}
那為什麼這個例子一定要處理它呢?例1不處理一樣也可以跑啊。
這時就要了解錯誤物件的繼承架構:
所有可拋的錯誤物件都繼承自java.lang.Throwable類別,它有兩個子類別:java.lang.Error與java.lang.Exception。
Error:表示嚴重的系統錯誤,也可用try catch捕捉,但最好不要。
Exception:是程式設計本身的錯誤,最好用它或它的子類別實例來處理。

回到剛剛的System.in.read(),in是System的靜態成員,翻一下API文件,或如果你用NetBeans,按著Ctrl+滑鼠點一下變數名稱,可以發現in的形態就是java.io.InputStream,而它裡面有個read()方法,實際程式碼:
public abstract int read() throws IOException;
而IOException是Exception的子類別,也就是說,除了RuntimeException和他以下的類別,IOException、ReflectiveOperationException、InterruptedException.....等等這些都是給編譯器檢查的,叫受檢例外(Checked Exception)通常受檢例外都要加上try catch

而RuntimeException衍生出來的類別實例,叫執行時期例外,又稱非受檢例外(Unchecked Exception),編譯器不會檢查,是你自己要檢查的。

所以,例2,就是一種RuntimeException。

public static void main(String[] args) {
        try {
            System.in.read();
        } 
        catch(Exception e) {
            e.printStackTrace();
        }
        catch(java.io.IOException e) {
            e.printStackTrace();
        } 
}
還有如果父類別物件比,子類別物件先被捕捉,那就會錯誤。

try {
    System.in.read();
} catch(java.io.IOException e) {
    e.printStackTrace();
} catch(Exception e) {
    e.printStackTrace();
}
必須改成這樣,子類別先做。

try {
    作一些事...
} catch(IOException | InterruptedException | ClassCastException e) {
    e.printStackTrace();
}
這叫多重捕捉

但多重捕捉不能有繼承關係:
try {
    作一些事...
} catch( Exception  | IOException ) {
    e.printStackTrace();
}

接著作者下篇要抓還是要拋?是講throws,這篇我真的看不太懂我就先不寫了...。

沒有留言:

張貼留言