Javaの例外処理 <- TetraCalibers
TetraCalibers :
    -   for : science student & programmer
Javaの例外処理
    この記事は一問一答問題集としても使えます。
    画面左下のボタンを押すと、桃色の文字が塗りつぶされます。 (もう一度押すと元に戻ります。)

     

    例外の分類

     

    checked例外
    Java実行環境以外で発生する例外
    例外処理は必須
    unchecked例外
    実行中のプログラムが原因で発生する例外(実行時例外)や、プログラムの例外処理では復旧できない例外
    例外処理は任意

     

    Java実行環境以外で発生する例外

     unchecked例外であり、Errorのサブクラスとして提供されている。

     

    AssertionError
    assert文を使用している際に、boolean式でfalseが返ると発生
    StackOverflowError
    アプリケーションでの再帰の回数が多すぎる場合に発生
    NoClassDefFoundError
    読み込もうとしたクラスファイルが見つからない場合に発生

     

    実行中のプログラムが原因で発生する例外

     unchecked例外であり、RuntimeExceptionのサブクラスとして提供されている。

     

    ArrayIndexOutOfBoundsException
    不正なインデックスで要素にアクセスしようとした場合に発生
    ArrayStoreException
    不正な型のオブジェクトを配列に格納した場合に発生
    ClassCastException
    参照変数において間違ったキャストを行った場合に発生
    IllegalStateException
    メソッドの呼び出しが正しくない状態で行われた場合に発生
    DateTimeException
    日付/時間の計算時に誤った処理を行った場合に発生
    MissingResourceException
    リソースが見つからない場合に発生
    ArithmeticException
    整数をゼロで除算した場合に発生
    NullPointerException
    nullが代入されている参照変数に対して、メソッド呼び出しを行った場合に発生
    NumberFormatException
    整数を表さない文字列を整数に変換しようとした場合に発生

     

    Java実行環境で発生し、例外処理では復旧できない例外

     checked例外であり、RuntimeException以外のExceptionのサブクラスとして提供されている。

     

    IOException
    入出力を行う場合に発生
    FileNotFoundException
    ファイル入出力において、目的のファイルがなかった場合に発生
    ParserException
    解析中に予想外のエラーがあった場合に発生
    SQLException
    データベース・アクセス時にエラーがあった場合に発生

     

    Throwableクラス

     

    Throwableクラス(擬似コード)

    class Throwable {
        void printStackTrace() {
            System.out.println(エラートレース(発生場所の詳細));
        }
        
        String getMessage() {
            return エラーメッセージ;
        }
    }
    

     

    try-catch-finallyによる例外処理

     

    • 例外が発生しそうな箇所をtryブロックで囲む
    • 例外が発生したときの処理をcatchブロックの中に記述
    • 例外の発生有無にかかわらず、必ず実行したい処理をfinallyブロックの中に記述

     tryブロックのみの記述はコンパイルエラーになる。

     catchブロックは複数定義できるが、指定した例外クラスに継承関係がある場合は、スーパークラス側から記述するとコンパイルエラーになる。

     

    SE7以降では、一つのcatchブロックの()内に複数の例外クラスを|で区切って列記できる。 この時、
    • 継承関係のある例外クラスは列記できない
    • キャッチした参照変数は暗黙的にfinalとなる

     

    throwsによる例外処理

     

    例外が発生する可能性のあるメソッドを定義する際に、発生する例外クラスをthrowsで指定しておくと、メソッド内で発生した例外オブジェクトはメソッドの呼び出し元に転送される。

     

    throwsの使用例

    public class Main {
        // OK
        static void haveUncheckedException throws ArrayStoreException {
            throw new ArrayStoreException();
        }
        
        // OK: unchecked例外は例外処理が任意であるため、
        //     例外処理をしていなくても呼び出し元に転送される
        static void haveUncheckedExceptionImplicit {
            throw new ArrayStoreException();
        }
        
        // OK
        static void haveCheckedException throws IOException {
            throw new IOException();
        }
        
        // ERROR: checked例外では、throwsによる例外処理で転送を指示する必要がある
        static void haveCheckedExceptionImplicit {
            throw new IOException();
        }
        
        public static void main(String[] args) {
            try {
                haveUncheckedException();
                haveCheckedException();
            } catch (ArrayStoreException | IOException e) {
                System.out.println(e);
                // RESULT: java.lang.ArrayStoreException
            }
        }
    }
    

     

    独自例外クラスの作成

     Exceptionクラスを継承したpublicなクラスとして定義する。

     

    自然数(1以上の整数)でなければエラー

    // 独自例外クラス
    class NotNaturalNumberException extends Exception {
        private int number;
        
        public void setNumber(int number) {
            this.number = number;
        }
        
        public int getNumber() {
            return this.number;
        }
    }
    
    public class Main {
        // 自然数でなければ独自例外を発生させる
        public static void isNaturalNumber(int number) throws NotNaturalNumberException {
            if (number < 0) {
                NotNaturalNumberException e = new NotNaturalNumberException();
                e.setNumber(number);
                throw e;
            }
        }
        
        public static void main(String[] args) {
            try {
                int num = -1;
                isNaturalNumber(num);
            } catch (NotNaturalNumberException e) {
                System.out.println("自然数ではない値を検出しました:" + e.getNumber());
            }
            // RESULT: 自然数ではない値を検出しました:-1
        }
    }
    

     

    再スロー(rethrow)

     

    再スロー(rethrow)
    スローされた例外をcatchブロックで一旦受け取り、その例外オブジェクトにエラーメッセージを追記したり、異なる例外クラスに変更したりした後、再度スローすること

     

    class NotOddNumberException extends Exception {
        NotOddNumberException() {
            super("奇数ではありません");
        }
    }
    
    class NotEvenNumberException extends Exception {
        NotEvenNumberException() {
            super("偶数ではありません");
        }
    }
    
    public class Main {
        public static void oddOrEven(int number) throws NotOddNumberException, NotEvenNumberException {
            try {
                if (number % 2 == 0) {
                    throw new NotOddNumberException();
                } else {
                    throw new NotEvenNumberException();
                }
            } catch (Exception e) {
                // 例外が起きた際に必要な処理を実装
                // 処理が終わったら、例外をスロー
                throw e;
            }
        }
        
        public static void main(String[] args) {
            try {
                oddOrEven(2);
            } catch (NotOddNumberException | NotEvenNumberException) {
                System.out.println(e.getMessage());
                // RESULT: 奇数ではありません
            }
        }
    }
    

     

    throws付メソッドのオーバーライド

     

    class Super {
        void method() throws IOException {
            System.out.println("IOExceptionをスロー");
        }
    }
    
    class OverrideExample1 extends Super {
        // OK: 元メソッドにthrowsがあっても、オーバーライドの際にthrowsを記述しないことは可能
        void method() {
            System.out.println("何もスローしない");
        }
    }
    
    class OverrideExample2 extends Super {
        // OK: 元メソッドと同じ例外クラスをスローするのは当然OK
        void method() {
            System.out.println("ここでもIOExceptionをスロー");
        }
    }
    
    class OverrideExample3 extends Super {
        // OK: 元メソッドがスローした例外クラスのサブクラスをスローするのはOK
        void method() throws FileNotFoundException {
            System.out.println("IOExceptionのサブクラスをスロー");
        }
    }
    
    class OverrideExample4 extends Super {
        void method() throws Exception {
            // ERROR: 元メソッドがスローした例外クラスのスーパークラスをスローするのはNG
            System.out.println("IOExceptionのスーパークラスをスロー);
        }
    }
    
    class OverrideExample5 extends Super {
        // OK: RuntimeExceptionをスローするのはOK
        void method() throws RuntimeException {
            System.out.println("RuntimeExceptionをスロー");
        }
    }
    
    class OverrideExample6 extends Super {
        // OK: RuntimeExceptionのサブクラスをスローするのはOK
        void method() throws NullPointerException {
            System.out.println("RuntimeExceptionのサブクラスをスロー");
        }
    }
    
    class OverrideExample7 extends Super {
        // ERROR: RuntimeExceptionともIOExceptionとも無関係なのでNG
        void method() throws SQLException {
            System.out.println("その他の例外クラスをスロー");
        }
    }
    

     

    try-with-resources

     

    import java.sql.*;
    
    class MyResource implements AutoCloseable {
        private String name;
        
        public MyResource(String name) {
            this.name = name;
        }
        
        public void close() throws Exception {
            System.out.println(name + " is closed");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            try (MyResource obj1 = new MyResource("obj1");
                 MyResource obj2 = new MyResource("obj2")) {
                System.out.println("tryのみでの使用が可能です");
            }
            // RESULT: tryのみでの使用が可能です
            //         obj2 is closed
            //         obj1 is closed
        }
    }
    

     

    抑制された例外

     

    import java.sql.*;
    
    class MyResource implements AutoCloseable {
        private String name;
        
        public MyResource(String name) {
            this.name = name;
        }
        
        public void method() throws SQLException {
            System.out.println("ここはmethod()、この後何かが起こるかも…");
            throw new SQLException("method()でエラー発生");
        }
        
        public void close() throws SQLException {
            System.out.println("ここはclose()、この後何かが起こるかも…");
            throw new SQLException("close()でエラー発生");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            try (MyResource obj = new MyResource("obj")) {
                obj.method();
            } catch (SQLException e) {
                System.out.println(e.getMessage());
                System.out.println("抑制された例外を取り出します");
                Throwable[] suppressedExceptions = e.getSuppressed();
                System.out.println("    抑制例外数:" + suppressedExceptions.length);
                for (Throwable ex: suppressedExceptions) {
                    System.out.println("    " + ex.getMessage());
                }
            }
        }
    }
    

     

    ここはmethod()、この後何かが起こるかも…
    ここはclose()、この後何かが起こるかも…
    method()でエラー発生
    抑制された例外を取り出します
    抑制例外数:1
    close()でエラー発生
    
    Category
    隠す