Java Silver試験で頻出の「例外処理」に関する問題を10問収録しました。try-catch-finally、検査例外と非検査例外、throws宣言、try-with-resources、例外の継承階層、マルチキャッチなど、試験で問われやすいポイントを網羅しています。
問題1 ⭐(基本)
次のコードをコンパイル・実行すると、どうなりますか?
public class Main {
public static void main(String[] args) {
try {
int result = 10 / 0;
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Caught: " + e.getMessage());
} finally {
System.out.println("Finally block");
}
}
}
- A.
Result: 0→Finally block - B.
Caught: / by zero→Finally block - C.
Caught: / by zero - D.
Finally blockのみ
解答と解説
正解: B
10 / 0はArithmeticExceptionをスローします。tryブロック内で例外が発生すると、それ以降の文(System.out.println("Result: " + result))は実行されません。
catchブロックで例外が捕捉され、e.getMessage()は"/ by zero"を返します。finallyブロックは例外の有無にかかわらず必ず実行されます。
finallyブロックが実行されないのは、System.exit()が呼ばれた場合やJVMが異常終了した場合のみです。
問題2 ⭐⭐(応用)
次のコードをコンパイル・実行すると、どうなりますか?
public class Main {
public static void main(String[] args) {
System.out.println(getValue());
}
static int getValue() {
try {
return 1;
} catch (Exception e) {
return 2;
} finally {
return 3;
}
}
}
- A.
1 - B.
2 - C.
3 - D. コンパイルエラー
解答と解説
正解: C
finallyブロック内のreturnは、tryやcatchブロック内のreturnを上書きします。tryブロックでreturn 1が実行されようとしますが、finallyブロックのreturn 3が最終的に実行されるため、メソッドは3を返します。
finallyブロック内でreturnを使うことは推奨されません。予期しない動作の原因になり、try/catchでスローされた例外も握りつぶしてしまいます。
問題3 ⭐⭐(応用)
次のコードをコンパイルすると、どうなりますか?
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try {
System.out.println("Hello");
} catch (IOException e) {
System.out.println("Caught IOException");
}
}
}
- A.
Helloが出力される - B. コンパイルエラー:
IOExceptionはtryブロックからスローされない - C.
Hello→Caught IOException - D. 実行時エラー
解答と解説
正解: B
検査例外(IOExceptionなど)をcatchする場合、コンパイラはtryブロック内のコードがその例外をスローする可能性があるかチェックします。System.out.println("Hello")はIOExceptionをスローしないため、到達不能なcatchブロックとしてコンパイルエラーになります。
ただし、非検査例外(RuntimeExceptionやそのサブクラス)のcatchは、tryブロックがその例外をスローしなくてもコンパイルエラーになりません。
// これはコンパイル可能
try {
System.out.println("Hello");
} catch (RuntimeException e) { // OK
System.out.println("Caught");
}
問題4 ⭐⭐⭐(チャレンジ)
次のコードをコンパイル・実行すると、どうなりますか?
public class Main {
public static void main(String[] args) {
try {
throw new RuntimeException("first");
} catch (RuntimeException e) {
System.out.println(e.getMessage());
throw new RuntimeException("second");
} finally {
System.out.println("finally");
}
}
}
- A.
first→finally→RuntimeException: secondがスローされる - B.
first→RuntimeException: secondがスローされる - C.
first→finally - D. コンパイルエラー
解答と解説
正解: A
実行順序:
-
tryブロックでRuntimeException("first")がスローされる -
catchブロックで捕捉され、"first"が出力される -
catchブロックでRuntimeException("second")がスローされる - ただし
finallyブロックが先に実行され、"finally"が出力される -
finallyブロック完了後、catchブロックからのRuntimeException("second")がスローされる
catchブロックで例外をスローしても、finallyブロックは必ず実行されます。mainメソッドで例外が処理されないため、最終的にスタックトレースが出力されます。
問題5 ⭐(基本)
次のコードをコンパイルすると、どうなりますか?
import java.io.IOException;
public class Main {
static void riskyMethod() throws IOException {
throw new IOException("File not found");
}
public static void main(String[] args) {
riskyMethod();
System.out.println("Done");
}
}
- A.
File not foundが出力される - B.
Doneが出力される - C. コンパイルエラー:
mainで検査例外が処理されていない - D. 実行時エラー
解答と解説
正解: C
riskyMethod()はthrows IOExceptionを宣言しています。IOExceptionは検査例外(ExceptionのサブクラスでRuntimeExceptionではない)なので、呼び出し側は以下のいずれかを行う必要があります:
-
try-catchで捕捉する -
throwsで上位に伝播させる
mainメソッドはどちらも行っていないため、コンパイルエラーになります。
修正例:
public static void main(String[] args) {
try {
riskyMethod();
} catch (IOException e) {
System.out.println(e.getMessage());
}
System.out.println("Done");
}
問題6 ⭐⭐(応用)
次のコードをコンパイル・実行すると、どうなりますか?
public class Main {
public static void main(String[] args) {
try {
String s = null;
System.out.println(s.length());
} catch (NullPointerException | ArithmeticException e) {
System.out.println("Caught: " + e.getClass().getSimpleName());
}
}
}
- A.
Caught: NullPointerException - B.
Caught: ArithmeticException - C. コンパイルエラー(マルチキャッチが不正)
- D. 実行時エラー
解答と解説
正解: A
sがnullなのでs.length()はNullPointerExceptionをスローします。マルチキャッチ(|で複数の例外型を指定)で捕捉されます。
マルチキャッチのルール:
-
|で区切って複数の例外型を指定できる - マルチキャッチの変数
eは暗黙的にfinal(再代入不可) - 継承関係にある例外を同時に指定できない(例:
IOException | Exceptionはコンパイルエラー)
// コンパイルエラーの例
catch (IOException | Exception e) { } // ExceptionはIOExceptionの親クラス
// マルチキャッチの変数は再代入不可
catch (NullPointerException | ArithmeticException e) {
e = new NullPointerException(); // コンパイルエラー
}
問題7 ⭐⭐⭐(チャレンジ)
次のコードをコンパイル・実行すると、どうなりますか?
public class Main {
public static void main(String[] args) {
try {
System.out.println("try");
throw new Exception();
} catch (Exception e) {
System.out.println("catch");
return;
} finally {
System.out.println("finally");
}
System.out.println("after try-catch");
}
}
- A.
try→catch→finally→after try-catch - B.
try→catch→finally - C.
try→catch→after try-catch - D. コンパイルエラー
解答と解説
正解: D
tryブロックはthrow new Exception()で必ず例外をスローするため正常完了しません。catchブロックはreturn文があるため正常完了しません。JLS 14.21(到達可能性ルール)により、try-catch-finallyブロック全体が正常完了する可能性がないため、その後のSystem.out.println("after try-catch")は**到達不能コード(unreachable statement)**となりコンパイルエラーになります。
もしcatchブロック内のreturnを削除すれば、catchブロックが正常完了するようになり、出力は try → catch → finally → after try-catch となります。
finallyブロックに関する重要なポイント:
-
finallyブロックはreturnがある場合でも、returnの前に実行される - ただし到達不能コードがあるとコンパイルエラーになる
問題8 ⭐⭐(応用)
次のコードをコンパイル・実行すると、どうなりますか?
public class MyResource implements AutoCloseable {
String name;
MyResource(String name) {
this.name = name;
System.out.println("Opening " + name);
}
@Override
public void close() {
System.out.println("Closing " + name);
}
public static void main(String[] args) {
try (MyResource r1 = new MyResource("A");
MyResource r2 = new MyResource("B")) {
System.out.println("Using resources");
}
}
}
- A.
Opening A→Opening B→Using resources→Closing A→Closing B - B.
Opening A→Opening B→Using resources→Closing B→Closing A - C.
Opening A→Opening B→Using resources - D. コンパイルエラー
解答と解説
正解: B
try-with-resourcesの重要なルール:
- リソースは宣言された逆順で
close()される -
r1(A)が先に開かれ、r2(B)が後に開かれる - クローズは逆順:
r2(B)が先にクローズ →r1(A)が後にクローズ
出力:
Opening A
Opening B
Using resources
Closing B
Closing A
逆順でクローズするのは、後に開いたリソースが先に開いたリソースに依存している可能性があるためです。リソースクラスはAutoCloseable(またはCloseable)インターフェースを実装する必要があります。
問題9 ⭐⭐⭐(チャレンジ)
次のうち、例外クラスの継承階層として正しいものはどれですか?
- A.
Object→Throwable→Exception→RuntimeException→Error - B.
Object→Throwable→Error/Exception(分岐)→Exceptionの下にRuntimeException - C.
Object→Exception→Throwable→RuntimeException - D.
Object→Throwable→RuntimeException→Exception
解答と解説
正解: B
Javaの例外階層:
Object
└── Throwable
├── Error(非検査、回復不能: OutOfMemoryError, StackOverflowError 等)
└── Exception(検査例外)
├── IOException
├── SQLException
└── RuntimeException(非検査例外)
├── NullPointerException
├── ArrayIndexOutOfBoundsException
├── ArithmeticException
├── ClassCastException
└── IllegalArgumentException
重要な分類:
-
検査例外:
ExceptionのサブクラスのうちRuntimeException以外 →try-catchまたはthrowsが必須 -
非検査例外:
RuntimeExceptionとそのサブクラス →try-catch/throwsは任意 -
エラー:
Errorとそのサブクラス → 通常はキャッチしない
問題10 ⭐⭐⭐(チャレンジ)
次のコードをコンパイル・実行すると、どうなりますか?
class ParentException extends Exception {}
class ChildException extends ParentException {}
public class Main {
public static void main(String[] args) {
try {
throw new ChildException();
} catch (ParentException e) {
System.out.println("ParentException caught");
} catch (ChildException e) {
System.out.println("ChildException caught");
}
}
}
- A.
ParentException caught - B.
ChildException caught - C.
ParentException caught→ChildException caught - D. コンパイルエラー
解答と解説
正解: D
catchブロックの順序に関するルールにより、コンパイルエラーになります。ChildExceptionはParentExceptionのサブクラスなので、最初のcatch (ParentException e)ですべてのChildExceptionも捕捉されます。結果として2番目のcatch (ChildException e)には到達できず、コンパイラはこれを検出してエラーにします。
catchブロックはサブクラスを先に、スーパークラスを後に記述する必要があります。
正しい順序:
try {
throw new ChildException();
} catch (ChildException e) { // サブクラスが先
System.out.println("ChildException caught");
} catch (ParentException e) { // スーパークラスが後
System.out.println("ParentException caught");
}
参考
- Oracle Java SE 17 Language Specification - Chapter 11: Exceptions
- Oracle Java Tutorials - Exceptions
- Oracle Java Tutorials - The try-with-resources Statement
@kotaro_ai_lab
AI活用や開発効率化について発信しています。フォローお気軽にどうぞ!