Java / LESSON 13 / JV13

Java:例外処理

1学習の目的

2基礎解説

ブロック役割実行されるタイミング
try失敗するかもしれない処理常に
catchエラーが起きたときの対処エラー時のみ
finally後始末必ず
throwエラーを発生させる明示的に
✅ 覚えるべき重要ポイント
  1. try の中でエラーが起きると、その先は飛ばして catch へ移る。プログラム全体が止まるのを防げる。
  2. finally は必ず実行されるreturn しても、例外が起きても実行されるので、ファイルを閉じるなどの後始末に使う。
  3. catch は具体的なものから先に書くException(すべての例外の親)を先に書くと、それ以降が到達不能になりコンパイルエラーになる。
  4. 検査例外は throws の宣言が必要Exception を継承した例外は、メソッドに throws を書かないとコンパイルが通らない——呼び出す側に対処を強制できる。
  5. ⚠ catch を空にしないcatch (Exception e) { } と書くと、エラーが起きても何も分からなくなる。最低でもメッセージは出力する

現場使用例:ファイルが見つからない、数値変換の失敗、ネットワークの切断、在庫不足などの業務エラー。

// ① 基本のtry-catch
try {
    int result = 10 / 0;           // ここでエラー
    System.out.println("ここは実行されない");
} catch (ArithmeticException e) {
    System.out.println(e.getMessage());   // "/ by zero"
}

// ② 複数のcatch(具体的なものから先に)
try {
    Integer.parseInt("abc");
} catch (NumberFormatException e) {       // 具体的
    System.out.println("数値ではありません");
} catch (Exception e) {                   // 一般的(後ろ)
    System.out.println("その他のエラー");
}

// ③ finally は必ず実行される
try {
    return "値";                  // return しても…
} finally {
    System.out.println("これは実行される");
}

// ④ 複数の型をまとめて捕まえる
try { … }
catch (IllegalArgumentException | IllegalStateException e) { … }

// ⑤ よく出会う例外
// ArithmeticException          … 0で割った
// NullPointerException         … null に対する操作
// NumberFormatException        … 数値変換の失敗
// ArrayIndexOutOfBoundsException … 配列の範囲外

// ⑥ 自作の例外(業務エラーを表現する)
static class OutOfStockException extends Exception {
    private int remaining;

    public OutOfStockException(String msg, int remaining) {
        super(msg);
        this.remaining = remaining;   // 追加情報を持てる
    }

    public int getRemaining() { return remaining; }
}

// throws で「失敗する可能性」を宣言する
static void order(int stock, int qty) throws OutOfStockException {
    if (qty > stock) {
        throw new OutOfStockException("在庫不足です", stock);
    }
}

// 呼び出す側は対処を強制される
try {
    order(5, 100);
} catch (OutOfStockException e) {
    System.out.println(e.getMessage() + " 残り" + e.getRemaining());
}

3基本ドリル(10問)

JV13-D01 0で割る処理を try-catch で捕まえ、エラーメッセージを出力せよ。 コーディング★☆☆無料
期待される結果

出力 == / by zero

ヒント

ArithmeticException を catch し、e.getMessage() を出力する。

模範解答
public class Main {
    public static void main(String[] args) {
        try {
            int result = 10 / 0;
            System.out.println(result);
        } catch (ArithmeticException e) {
            System.out.println(e.getMessage());
        }
    }
}
解説

エラーが起きても処理が続く。catch が無ければ、この時点でプログラムが停止していた。

JV13-D02 finally ブロックはいつ実行されるか。
A. エラー時のみ B. 正常時のみ C. 必ず実行される D. 実行されない
選択★☆☆無料
期待される結果

解答 == C

ヒント

後始末のためのブロック。

模範解答
C
解説

return しても、例外が起きても実行される。ファイルを閉じる・接続を切るなどの後始末に使う。

JV13-D03 文字列 "abc" を数値に変換しようとして失敗を捕まえ、「数値に変換できません」と出力せよ。 コーディング★☆☆無料
期待される結果

出力 == 数値に変換できません

ヒント

Integer.parseInt は NumberFormatException を投げる。

模範解答
public class Main {
    public static void main(String[] args) {
        try {
            int n = Integer.parseInt("abc");
            System.out.println(n);
        } catch (NumberFormatException e) {
            System.out.println("数値に変換できません");
        }
    }
}
解説

入力値の変換は必ず失敗を想定する。ユーザーが何を入力するかは分からないので、try-catch で守るのが基本。

JV13-D04 エラーを捕まえるキーワードを補ってコードを完成させよ。 穴埋め★☆☆無料
コード
public class Main {
    public static void main(String[] args) {
        try {
            int[] a = new int[2];
            System.out.println(a[5]);
        } ____ (ArrayIndexOutOfBoundsException e) {
            System.out.println("範囲外です");
        }
    }
}
期待される結果

出力 == 範囲外です

ヒント

「捕まえる」を意味する5文字のキーワード。

模範解答
catch
解説

catch で例外の種類を指定して受け取る。指定した型と一致する例外だけが捕まえられる。

JV13-D05 catch を複数書くとき、正しい順序はどれか。
A. Exception を最初に B. 具体的な例外を最初に C. 順序は自由 D. アルファベット順
選択★☆☆無料
期待される結果

解答 == B

ヒント

Exception はすべての例外の親。

模範解答
B
解説

Exception を先に書くと、後ろの catch が到達不能になってコンパイルエラー。具体的なものから順に並べる。

JV13-D06 finally を使い、正常時でも「後始末」が実行されることを確認せよ(2行出力:「処理完了」「後始末」)。 コーディング★★☆広告解放
期待される結果

出力2行。処理完了 / 後始末

ヒント

try の中で出力し、finally でも出力する。

模範解答
public class Main {
    public static void main(String[] args) {
        try {
            System.out.println("処理完了");
        } catch (Exception e) {
            System.out.println("エラー");
        } finally {
            System.out.println("後始末");
        }
    }
}
解説

エラーが起きなくても finally は実行される。「成功しても失敗しても必ずやること」を書く場所。

JV13-D07 例外を発生させるキーワードを補ってコードを完成させよ。 穴埋め★★☆広告解放
コード
public class Main {
    public static void main(String[] args) {
        try {
            ____ new IllegalArgumentException("不正な値");
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
        }
    }
}
期待される結果

出力 == 不正な値

ヒント

「投げる」を意味する5文字。

模範解答
throw
解説

throw で意図的に例外を発生させる。業務ルールに違反したときなど、処理を続けるべきでない場面で使う。

JV13-D08 catch (Exception e) { } と空で書くのが良くない理由はどれか。
A. 遅くなる B. エラーが起きても気づけない C. コンパイルエラー D. メモリを使う
選択★★☆広告解放
期待される結果

解答 == B

ヒント

何も出力しないとどうなるか。

模範解答
B
解説

エラーが握りつぶされる。動かない原因が分からず、調査に何時間もかかる——実務で最も嫌われる書き方の1つ。

JV13-D09 自作の例外クラス OutOfStockException を作れ(Exception を継承、残り在庫数を持つ)。在庫5に100個注文して捕まえ、「在庫不足です 残り5」と出力すること。 コーディング★★☆広告解放
期待される結果

出力 == 在庫不足です 残り5

ヒント

class OutOfStockException extends Exception { ... } でフィールドとgetterを持たせる。

模範解答
public class Main {

    static class OutOfStockException extends Exception {
        private int remaining;

        public OutOfStockException(String msg, int remaining) {
            super(msg);
            this.remaining = remaining;
        }

        public int getRemaining() {
            return remaining;
        }
    }

    static void order(int stock, int qty) throws OutOfStockException {
        if (qty > stock) {
            throw new OutOfStockException("在庫不足です", stock);
        }
    }

    public static void main(String[] args) {
        try {
            order(5, 100);
        } catch (OutOfStockException e) {
            System.out.println(e.getMessage() + " 残り" + e.getRemaining());
        }
    }
}
解説

例外に追加情報を持たせられる。「在庫不足」というメッセージだけでなく、「残り何個か」まで呼び出し元に伝えられる。

JV13-D10 複数の例外をまとめて捕まえよ(| で区切り、IllegalStateException を投げて「状態エラー」と出力)。 コーディング★★☆広告解放
期待される結果

出力 == 状態エラー

ヒント

catch (A | B e) の形で複数の型を並べられる。

模範解答
public class Main {
    public static void main(String[] args) {
        try {
            throw new IllegalStateException("状態エラー");
        } catch (IllegalArgumentException | IllegalStateException e) {
            System.out.println(e.getMessage());
        }
    }
}
解説

同じ対処をする例外はまとめて書ける。catch を何個も並べて同じ処理を繰り返す必要がない。

4実践シナリオ(5問)

JV13-S01 【入力値の検証】文字列配列 {"128000", "abc", "3200"} を数値に変換し、失敗したものはスキップして合計を出力せよ(131200)。 コーディング★★☆広告解放
期待される結果

出力 == 131200

ヒント

ループの中で try-catch し、失敗したら continue する。

模範解答
public class Main {
    public static void main(String[] args) {
        String[] inputs = {"128000", "abc", "3200"};

        int total = 0;
        for (String s : inputs) {
            try {
                total += Integer.parseInt(s);
            } catch (NumberFormatException e) {
                continue;
            }
        }

        System.out.println(total);
    }
}
解説

1件失敗しても全体を止めない。CSVの取り込みなど、一部に不正データが混ざる場面で有効な書き方。

JV13-S02 【エラーの種類で分岐】複数の catch を使い、NumberFormatException と ArithmeticException を別々のメッセージで処理せよ(0除算を起こして「計算エラー」と出力)。 コーディング★★☆広告解放
期待される結果

出力 == 計算エラー

ヒント

具体的な例外から順に catch を並べる。

模範解答
public class Main {
    public static void main(String[] args) {
        try {
            int n = Integer.parseInt("10");
            System.out.println(n / 0);
        } catch (NumberFormatException e) {
            System.out.println("変換エラー");
        } catch (ArithmeticException e) {
            System.out.println("計算エラー");
        }
    }
}
解説

エラーの種類ごとに対処を変えられる。変換の失敗なら再入力を促し、計算エラーなら処理を見直す——原因に応じた対応ができる。

JV13-S03 【finally で後始末】ファイルを開く処理を模した例で、エラーが起きても閉じることを確認せよ(3行:「開く」「エラー発生」「閉じる」)。 コーディング★★☆広告解放
期待される結果

出力3行

ヒント

try で開いてエラーを起こし、finally で閉じる。

模範解答
public class Main {
    public static void main(String[] args) {
        try {
            System.out.println("開く");
            throw new RuntimeException("失敗");
        } catch (RuntimeException e) {
            System.out.println("エラー発生");
        } finally {
            System.out.println("閉じる");
        }
    }
}
解説

エラーが起きても必ず閉じられる。ファイルや接続を開いたまま放置すると、リソースが枯渇してシステム全体が止まる。

JV13-S04 【業務例外の設計】自作例外 InvalidPriceException を作り(不正な価格を保持)、-100を設定しようとして「価格が不正です:-100」と出力せよ。 コーディング★★★広告解放
期待される結果

出力 == 価格が不正です:-100

ヒント

例外に不正だった値を持たせ、getter で取り出す。

模範解答
public class Main {

    static class InvalidPriceException extends Exception {
        private int value;

        public InvalidPriceException(int value) {
            super("価格が不正です");
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    }

    static void setPrice(int price) throws InvalidPriceException {
        if (price < 0) {
            throw new InvalidPriceException(price);
        }
    }

    public static void main(String[] args) {
        try {
            setPrice(-100);
        } catch (InvalidPriceException e) {
            System.out.println(e.getMessage() + ":" + e.getValue());
        }
    }
}
解説

何が不正だったかを例外が持っている。ログに残すときも、ユーザーに伝えるときも、具体的な値があると原因究明が早い。

JV13-S05 【throws で伝播】メソッドが例外を投げることを throws で宣言し、呼び出し元で対処する形を作れ(在庫3に10個注文して「注文できません」と出力)。 コーディング★★★広告解放
期待される結果

出力 == 注文できません

ヒント

static void order(...) throws Exception の形で宣言する。

模範解答
public class Main {

    static class OrderException extends Exception {
        public OrderException(String msg) {
            super(msg);
        }
    }

    static void order(int stock, int qty) throws OrderException {
        if (qty > stock) {
            throw new OrderException("注文できません");
        }
        System.out.println("注文成功");
    }

    public static void main(String[] args) {
        try {
            order(3, 10);
        } catch (OrderException e) {
            System.out.println(e.getMessage());
        }
    }
}
解説

throws を書くと、呼び出す側は必ず対処を求められる。try-catch を書かなければコンパイルが通らないので、エラーの見落としが起きない。

5仕上げ課題

JV13-FINAL 【堅牢な注文処理】例外処理を使い、あらゆる失敗に対応する注文システムを作れ。

自作例外 OrderException(Exception を継承)
・メッセージとエラーコード(String)を持つ
getCode() でコードを取得

メソッド processOrder(String qtyInput, int stock, int price)throws OrderException
qtyInput を数値に変換(失敗なら コード E01「数量が数値ではありません」)
② 数量が0以下なら コード E02「数量が不正です」
③ 数量が在庫を超えたら コード E03「在庫不足です」
④ 成功なら合計金額を返す(int)

main の処理:4件の注文を順に処理し、結果を出力(finally で毎回「---」を出力
・"2", 在庫5, 単価128000 → 成功
・"abc", 在庫5, 単価128000 → E01
・"0", 在庫5, 単価128000 → E02
・"100", 在庫5, 単価128000 → E03

期待される出力(8行)
成功:256,000円
---
[E01] 数量が数値ではありません
---
[E02] 数量が不正です
---
[E03] 在庫不足です
---
総合★★★広告解放
期待される結果

出力8行が完全一致

ヒント

parseInt の失敗は NumberFormatException を catch して、OrderException に変換して投げ直す。ループの中で try-catch-finally を書けば、毎回 --- が出力される。

模範解答
public class Main {

    static class OrderException extends Exception {
        private String code;

        public OrderException(String code, String message) {
            super(message);
            this.code = code;
        }

        public String getCode() {
            return code;
        }
    }

    static int processOrder(String qtyInput, int stock, int price)
            throws OrderException {

        int qty;
        try {
            qty = Integer.parseInt(qtyInput);
        } catch (NumberFormatException e) {
            throw new OrderException("E01", "数量が数値ではありません");
        }

        if (qty <= 0) {
            throw new OrderException("E02", "数量が不正です");
        }

        if (qty > stock) {
            throw new OrderException("E03", "在庫不足です");
        }

        return price * qty;
    }

    public static void main(String[] args) {
        String[] inputs = {"2", "abc", "0", "100"};

        for (String input : inputs) {
            try {
                int total = processOrder(input, 5, 128000);
                System.out.println("成功:" + String.format("%,d", total) + "円");
            } catch (OrderException e) {
                System.out.println("[" + e.getCode() + "] " + e.getMessage());
            } finally {
                System.out.println("---");
            }
        }
    }
}
解説

4件の注文のうち3件が失敗したが、プログラムは最後まで動き切った。これが例外処理の価値だ。1件の失敗が全体を止めない——バッチ処理や一括取り込みでは決定的に重要になる。

注目してほしいのは NumberFormatException を捕まえて OrderException投げ直している点だ。parseInt が投げる例外は「Javaの技術的なエラー」だが、呼び出し元が知りたいのは「注文処理のどこで失敗したか」である。技術的な例外を業務的な例外に変換する——これは実務で頻繁に使う設計手法だ。

エラーコード(E01・E02・E03)を持たせたのにも理由がある。メッセージは人間が読むもの、コードは機械が判定するものだ。ログを集計するとき「E03が今月50件」と数えられるし、多言語対応するときもコードは変わらない。

finally がループの各回で実行される点も確認できた。成功しても失敗しても必ず区切り線が出る——ファイルを閉じる、接続を切る、ログを書き出すといった後始末は、この場所に書く。

次章ではファイル入出力を学ぶ。例外処理が必須になる領域であり、今回学んだことが直接役立つ。