Java Silver SE17の例外処理問題10選|try-catch-finallyの実行順を解説

Java Silver SE17の例外処理問題10選をtry-catch-finallyのコードと猫のイラストで紹介する画像

Java Silver SE17の例外処理では、例外クラスの名前を暗記するだけでは問題を解けません。

特に間違えやすいのが、次のような問題です。

  • tryの途中で例外が発生したら、残りの処理は実行されるのか
  • 複数のcatchは、どの順番で判定されるのか
  • returnがあってもfinallyは実行されるのか
  • コンパイルエラーと実行時エラーのどちらになるのか
  • checked例外は、どこで処理する必要があるのか

この記事では、Java Silver SE17対策として押さえておきたい例外処理問題を10問用意しました。

最初にコードだけを見て、自分で実行結果を予想してください。そのあとに「答えと解説を見る」を開き、考え方を確認しましょう。

各コードは、それぞれ独立したプログラムです。選択肢を確認してから答えを開いてください。

目次

Java Silverの例外処理で間違えやすい5パターン

問題を解く前に、頻出パターンを整理します。

1. 例外発生後もtryブロックを読み続けてしまう

tryブロック内で例外が発生すると、その位置から対応するcatchへ処理が移ります。例外が発生した行より後ろにあるtry内の処理は実行されません。

2. catchを上から順番に確認していない

複数のcatchがある場合、上から順番に判定され、最初に一致したものだけが実行されます。

ただし、親クラスのcatchを子クラスより先に書くと、子クラス側へ到達できないためコンパイルエラーになります。

3. finallyを「例外発生時だけ動く」と考えてしまう

finallyは、例外が発生した場合だけでなく、発生しなかった場合にも原則として実行されます。trycatchreturnがあっても、メソッドを抜ける前に実行されます。

4. コンパイルエラーと実行時エラーを混同する

たとえば、Integer.parseInt("abc")はコンパイルできますが、実行するとNumberFormatExceptionが発生します。

一方、checked例外を処理も宣言もせずに呼び出すコードや、catchの順番が不正なコードはコンパイルエラーです。

5. checked例外とunchecked例外を同じように扱う

checked例外は、catchで処理するかthrowsで宣言する必要があります。RuntimeExceptionとそのサブクラスはunchecked例外なので、この強制はありません。

それでは問題を解いていきましょう。

問題1:try内で例外が発生した後の実行順

public class Main {
    public static void main(String[] args) {
        try {
            System.out.print("A");
            int num = 10 / 0;
            System.out.print("B");
        } catch (ArithmeticException e) {
            System.out.print("C");
        }
        System.out.print("D");
    }
}

選択肢:

  1. ABCD
  2. ACD
  3. AC
  4. コンパイルエラー
答えと解説を見る

正解は 2. ACD です。

最初のSystem.out.print("A")は正常に実行されます。次の10 / 0ArithmeticExceptionが発生し、処理は対応するcatchへ移ります。

したがって、例外が発生した行より後ろにあるSystem.out.print("B")は実行されません。catchCを出力したあと、try-catch文の次にある処理へ進み、Dが出力されます。

実行順は次のとおりです。

A → 例外発生 → CD

問題2:例外が発生しない場合のfinally

public class Main {
    public static void main(String[] args) {
        try {
            System.out.print("A");
            int num = 10 / 2;
            System.out.print(num);
        } catch (ArithmeticException e) {
            System.out.print("B");
        } finally {
            System.out.print("C");
        }
    }
}

選択肢:

  1. A5
  2. A5C
  3. ABC
  4. AC
答えと解説を見る

正解は 2. A5C です。

10 / 2では例外が発生しないため、tryブロックが最後まで実行されます。catchは実行されません。

その後、例外の有無にかかわらずfinallyが実行され、Cが出力されます。

「例外が起きなければfinallyは動かない」という理解は誤りです。

問題3:どのcatchが実行されるか

public class Main {
    public static void main(String[] args) {
        try {
            String value = null;
            System.out.print(value.length());
        } catch (NullPointerException e) {
            System.out.print("A");
        } catch (RuntimeException e) {
            System.out.print("B");
        } catch (Exception e) {
            System.out.print("C");
        }
    }
}

選択肢:

  1. A
  2. AB
  3. ABC
  4. コンパイルエラー
答えと解説を見る

正解は 1. A です。

nullが入った変数に対してlength()を呼び出すため、NullPointerExceptionが発生します。

複数のcatchは上から順番に確認されます。最初のcatch (NullPointerException e)が一致するため、Aだけが出力されます。

一度いずれかのcatchが実行されると、後続のcatchは実行されません。条件分岐のように、複数のcatchが続けて実行されるわけではありません。

問題4:catchの並び順とコンパイルエラー

public class Main {
    public static void main(String[] args) {
        try {
            int[] values = {10, 20};
            System.out.print(values[2]);
        } catch (RuntimeException e) {
            System.out.print("A");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.print("B");
        }
    }
}

選択肢:

  1. A
  2. B
  3. AB
  4. コンパイルエラー
答えと解説を見る

正解は 4. コンパイルエラー です。

ArrayIndexOutOfBoundsExceptionRuntimeExceptionのサブクラスです。

先に親クラスであるRuntimeExceptionを捕捉すると、その後ろにあるArrayIndexOutOfBoundsExceptioncatchへ到達する可能性がありません。そのため、コンパイラに到達不能なcatchと判断されます。

正しい順番は、対象範囲が狭い子クラスから広い親クラスです。

} catch (ArrayIndexOutOfBoundsException e) {
    System.out.print("B");
} catch (RuntimeException e) {
    System.out.print("A");
}

問題5:catchでreturnしてもfinallyは動くか

public class Main {
    static int test() {
        try {
            return 10 / 0;
        } catch (ArithmeticException e) {
            System.out.print("A");
            return 20;
        } finally {
            System.out.print("B");
        }
    }

    public static void main(String[] args) {
        System.out.print(test());
    }
}

選択肢:

  1. A20
  2. AB20
  3. Aのあと例外で終了する
  4. コンパイルエラー
答えと解説を見る

正解は 2. AB20 です。

try10 / 0ArithmeticExceptionが発生します。catchAを出力し、戻り値として20を返そうとします。

ただし、実際にメソッドを抜ける前にfinallyが実行されるため、次にBが出力されます。その後、test()の戻り値20mainメソッドで出力されます。

実行順は次のとおりです。

AB20

問題6:finallyにもreturnがある場合

public class Main {
    static int test() {
        try {
            return 10;
        } finally {
            return 20;
        }
    }

    public static void main(String[] args) {
        System.out.print(test());
    }
}

選択肢:

  1. 10
  2. 20
  3. 1020
  4. コンパイルエラー
答えと解説を見る

正解は 2. 20 です。

tryで戻り値10が決まったあと、メソッドを抜ける前にfinallyが実行されます。finallyにもreturn 20があるため、先に決まっていた戻り値10は破棄され、最終的に20が返されます。

試験問題としては重要ですが、実務でfinallyreturnを書くのは避けるべきです。trycatchの戻り値だけでなく、発生した例外まで隠してしまう可能性があるからです。

問題7:unchecked例外はthrowsなしで投げられるか

public class Main {
    static void check(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("invalid");
        }
    }

    public static void main(String[] args) {
        try {
            check(-1);
            System.out.print("A");
        } catch (IllegalArgumentException e) {
            System.out.print("B");
        }
    }
}

選択肢:

  1. A
  2. B
  3. 実行時に例外が捕捉されず終了する
  4. checkthrowsがないためコンパイルエラー
答えと解説を見る

正解は 2. B です。

IllegalArgumentExceptionRuntimeExceptionのサブクラスであり、unchecked例外です。そのため、checkメソッドの宣言にthrows IllegalArgumentExceptionを書かなくてもコンパイルできます。

check(-1)によって例外が投げられ、対応するcatchで捕捉されるためBが出力されます。例外発生後、try内のAは実行されません。

問題8:multi-catchに継承関係がある例外を書く

public class Main {
    public static void main(String[] args) {
        try {
            Integer.parseInt("abc");
        } catch (NumberFormatException | IllegalArgumentException e) {
            System.out.print("A");
        }
    }
}

選択肢:

  1. A
  2. 何も表示されない
  3. 実行時にNumberFormatExceptionで終了する
  4. コンパイルエラー
答えと解説を見る

正解は 4. コンパイルエラー です。

NumberFormatExceptionIllegalArgumentExceptionのサブクラスです。multi-catchの選択肢には、継承関係にある例外クラスを同時に指定できません。

この場合は、親クラスであるIllegalArgumentExceptionだけを指定すればNumberFormatExceptionも捕捉できます。

} catch (IllegalArgumentException e) {
    System.out.print("A");
}

なお、multi-catchで問題なく並べられるのは、たとえばIOException | SQLExceptionのように、互いに親子関係ではない例外です。

問題9:checked例外を処理しないコード

import java.io.IOException;

public class Main {
    static void read() throws IOException {
        throw new IOException();
    }

    public static void main(String[] args) {
        read();
        System.out.print("A");
    }
}

選択肢:

  1. A
  2. 実行時にIOExceptionで終了する
  3. 何も表示されず正常終了する
  4. コンパイルエラー
答えと解説を見る

正解は 4. コンパイルエラー です。

IOExceptionはchecked例外です。read()を呼び出す側は、次のどちらかで対応しなければなりません。

  1. try-catchで例外を処理する
  2. 呼び出し元のメソッドにもthrows IOExceptionを宣言する

このmainメソッドはどちらも行っていないため、プログラムを実行する前のコンパイル段階でエラーになります。

たとえば、次のようにすればコンパイルできます。

public static void main(String[] args) {
    try {
        read();
    } catch (IOException e) {
        System.out.print("B");
    }
}

問題10:catchで別の例外が発生した場合のfinally

public class Main {
    public static void main(String[] args) {
        try {
            System.out.print("A");
            throw new IllegalArgumentException();
        } catch (IllegalArgumentException e) {
            System.out.print("B");
            throw new RuntimeException();
        } finally {
            System.out.print("C");
        }
    }
}

選択肢:

  1. ABCと表示され、正常終了する
  2. ABと表示されたあと、RuntimeExceptionで終了する
  3. ABCと表示されたあと、RuntimeExceptionで終了する
  4. コンパイルエラー
答えと解説を見る

正解は 3. ABCと表示されたあと、RuntimeExceptionで終了する です。

まずtryAを出力し、IllegalArgumentExceptionが投げられます。対応するcatchへ移動してBを出力しますが、そこで新しくRuntimeExceptionが投げられます。

そのままメソッドを抜ける前にfinallyが実行されるため、Cも出力されます。その後、catchから投げられたRuntimeExceptionを処理する場所がないため、異常終了します。

finallyが実行されたからといって、未処理の例外が消えるわけではありません。

catchの並び順を見抜く方法

複数のcatchが登場したら、例外クラスの継承関係を確認します。

Object
  └─ Throwable
      └─ Exception
          └─ RuntimeException
              ├─ ArithmeticException
              ├─ NullPointerException
              └─ IllegalArgumentException
                  └─ NumberFormatException

基本ルールは、子クラスを上、親クラスを下です。

try {
    // 処理
} catch (NumberFormatException e) {
    // より具体的な例外
} catch (IllegalArgumentException e) {
    // より広い例外
} catch (RuntimeException e) {
    // さらに広い例外
}

問題を見たら、次の順番で判断しましょう。

  1. 発生する例外クラスを特定する
  2. catchを上から確認する
  3. 最初に一致するcatchを選ぶ
  4. 親クラスが子クラスより上にないか確認する

Java言語仕様でも、例外ハンドラは左から、つまり記述された順番に検査されます。また、前のcatchで同じ例外またはその親クラスを捕捉できる場合、後ろのcatchはコンパイルエラーになります。

finallyとreturnの関係

Java Silverでは、returnを見つけた瞬間に答えを決めないことが重要です。

次の順番で考えます。

  1. tryまたはcatchで戻り値が決まる
  2. メソッドを抜ける前にfinallyが実行される
  3. finallyが正常終了すれば、先に決まった値を返す
  4. finallyにもreturnがあれば、その値で上書きされる
  5. finallyで例外が発生すれば、元のreturnは完了しない

特に次の2つを区別してください。

try {
    return 10;
} finally {
    System.out.print("A");
}

これはAを出力したあと、10を返します。

try {
    return 10;
} finally {
    return 20;
}

こちらはfinallyreturnが優先され、20を返します。

ただし、実務ではfinally内のreturnは避けましょう。制御の流れが分かりにくくなり、例外を隠す原因にもなります。

checked例外とunchecked例外の違い

比較項目 checked例外 unchecked例外
主なクラス IOExceptionSQLExceptionなど RuntimeExceptionとそのサブクラス
catchまたはthrows 必須 必須ではない
検出される場面 未処理ならコンパイル時 主に実行時
ファイル操作、DB操作など 0除算、不正な添字、null参照など

覚え方はシンプルです。

RuntimeExceptionの仲間ならunchecked、それ以外のExceptionの仲間は基本的にcheckedと判断します。

代表例も押さえておきましょう。

unchecked例外の代表例

  • ArithmeticException:整数の0除算など
  • NullPointerExceptionnull参照に対するメソッド呼び出しなど
  • ArrayIndexOutOfBoundsException:配列の範囲外アクセス
  • NumberFormatException:数値に変換できない文字列の変換
  • ClassCastException:不正な型キャスト

checked例外の代表例

  • IOException
  • FileNotFoundException
  • SQLException
  • ClassNotFoundException

checked例外があるコードでは、catchまたはthrowsを探してください。どちらもなければ、コンパイルエラーを疑います。

Java Silverの例外処理問題を解く手順

本番で長いコードが出ても、上から漫然と読む必要はありません。

次の5点に印を付けてください。

  1. コンパイルエラーになる要素はないか
  2. 最初に例外が発生する行はどこか
  3. 例外発生後にスキップされる処理はどこか
  4. 一致するcatchはどれか
  5. finallyreturnはあるか

特に、選択肢に「コンパイルエラー」と「実行時に例外」が両方ある場合は要注意です。

  • 文法や例外処理の義務に問題がある → コンパイルエラー
  • コンパイルは通るが、実行中に例外が発生する → 実行時エラー

この区別ができるだけでも、選択肢をかなり絞れます。

10問の正答一覧

問題 正解 学習ポイント
1 ACD 例外発生後のtryは実行されない
2 A5C 例外がなくてもfinallyは実行される
3 A 最初に一致したcatchだけが動く
4 コンパイルエラー catchは子クラスから書く
5 AB20 returnの前にfinallyが動く
6 20 finallyのreturnが優先される
7 B unchecked例外にthrowsは必須でない
8 コンパイルエラー multi-catchに親子関係の型は並べられない
9 コンパイルエラー checked例外はcatchまたはthrowsが必要
10 ABCのあと例外終了 catchから例外が出てもfinallyは動く

まとめ:例外処理は実行順とコンパイル可否を分けて考えよう

Java Silver SE17の例外処理問題では、次のポイントが重要です。

  • 例外が発生すると、tryの残りは実行されない
  • 複数のcatchは上から判定され、最初に一致したものだけが動く
  • catchは原則として子クラスから親クラスの順番で書く
  • finallyは例外がなくても、returnがあっても原則実行される
  • checked例外はcatchまたはthrowsが必要
  • コンパイルエラーと実行時エラーを区別する

知識を覚えたら、実際のコード問題で「どこまで実行されるか」を追う練習が必要です。

当サイトでは、Java Silver SE17対応のコード読解問題と模擬試験を無料で公開しています。登録不要なので、例外処理以外の分野も続けて確認してみてください。

Java Silver SE17の無料問題を解く

参考資料

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

tibiyaのアバター tibiya ITエンジニア

文系卒エンジニア(8年目)
Java / JavaScript / C# を中心に業務システム開発を経験。
Java資格・IT就活・文系エンジニアの実体験を発信しています。

コメント

コメントする

目次