検査例外と非検査エラー
CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 61/87。
Javaの例外階層がいくつかの異なるブランチに分かれていることを見てきました。理解しておくべき重要な違いは、checked(検査例外)と unchecked(非検査例外)の違いです。これは、コード内での例外の処理方法に影響します。
検査例外は、コンパイラが処理を強制する例外です。これらは Exception を直接継承します(RuntimeException を経由しません)。これらはキャッチするか、throws を使って宣言する必要があります:
// IOExceptionを処理する必要があります - これはチェック例外です
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path); // IOExceptionをスローする可能性があります
}
// または、それをキャッチします
public void readFileSafe(String path) {
try {
FileReader reader = new FileReader(path);
} catch (IOException e) {
System.out.println("File error: " + e.getMessage());
}
}非検査例外は RuntimeException を継承します。コンパイラはそれらの処理を要求しません。これらは通常、null参照や無効な配列アクセスのようなプログラミングエラーを示します:
// throws 宣言は不要です
public int divide(int a, int b) {
return a / b; // ArithmeticException がスローされる可能性があります (非検査例外)
}
String name = null;
name.length(); // NullPointerException (非検査例外)カスタム例外を作成する際は、呼び出し側にどのように処理させたいかに基づいて親クラスを選択してください。呼び出し側が予期すべき回復可能な条件には検査例外を使用し、正しいコードでは発生しないはずのプログラミングエラーには非検査例外を使用してください。
チャレンジ
簡単検査例外(checked exception)と非検査例外(unchecked exception)の主な違いを実証する年齢確認システムを構築しましょう!両方のタイプのカスタム例外を作成し、コンパイラがそれらをどのように異なる方法で処理するかを確認します。
コードは4つのファイルに分けて構成します:
InvalidAgeException.java: 年齢検証の失敗に対する検査例外を作成します。この例外はExceptionを直接継承する必要があり、呼び出し元はこれを処理することを強制されます。メッセージを受け取り、それを親クラスに渡すコンストラクタを含めてください。NegativeAgeException.java: 負の年齢が提供された場合(正しい入力では発生しないはずのプログラミングエラー)の非検査例外を作成します。これはRuntimeExceptionを継承する必要があります。メッセージを受け取り、親に渡すコンストラクタを含めてください。AgeVerifier.java: 両方の例外タイプを使用して年齢を検証するクラスを作成します。2つの static メソッドを含めてください:verifyAge(int age)- このメソッドは、年齢が 0 未満の場合、メッセージ"Age cannot be negative: " + ageを持つNegativeAgeException(非検査)をスローする必要があります。年齢が 18 未満の場合は、メッセージ"Must be 18 or older: " + ageを持つInvalidAgeException(検査)をスローします。それ以外の場合は、"Age " + age + " verified successfully"と出力します。このメソッドは検査例外をスローするため、throws InvalidAgeExceptionを宣言する必要があります。verifyAgeUncheckedOnly(int age)- このメソッドは非検査例外のみを使用します。年齢が負の場合、同じメッセージ形式でNegativeAgeExceptionをスローします。年齢が 18 未満の場合は、メッセージ"Too young: " + ageを持つIllegalArgumentExceptionをスローします。それ以外の場合は、"Age " + age + " verified (unchecked method)"と出力します。このメソッドにはthrows宣言が必要ないことに注目してください!Main.java: 検証システムをまとめましょう!1つの入力として年齢(整数)を受け取ります。まず、
"=== Checked Exception Method ==="と出力し、入力された年齢でverifyAgeを呼び出します。検査例外をスローするため、try-catch で囲む必要があります。InvalidAgeExceptionをキャッチし、"Checked exception caught: " + e.getMessage()と出力します。また、NegativeAgeExceptionもキャッチし、"Unchecked exception caught: " + e.getMessage()と出力します。次に、空行と
"=== Unchecked Exception Method ==="を出力します。同じ年齢でverifyAgeUncheckedOnlyを呼び出します。このメソッドは例外をスローする可能性がありますが、キャッチを強制されることはありません。しかし、エラーを適切に処理するために try-catch で囲んでください。NegativeAgeExceptionをキャッチし、"Runtime exception caught: " + e.getMessage()と出力します。IllegalArgumentExceptionをキャッチし、"Illegal argument caught: " + e.getMessage()と出力します。
入力として、検証する年齢を表す1つの整数を受け取ります。
主な違いに注目してください。検査例外を持つメソッドは throws 宣言を必要とし、呼び出し元にその処理を強制しますが、非検査メソッドは例外処理がなくても問題なくコンパイルされます。これが Java における検査例外と非検査例外の根本的な違いです!
チートシート
Javaの例外は、検査例外 (checked exceptions)と非検査例外 (unchecked exceptions)に分けられ、これによってコンパイラがどのように例外処理を強制するかが決まります。
検査例外は、Exceptionを直接継承します(RuntimeExceptionを経由しません)。コンパイラは、これらをキャッチするか、throwsを使用して宣言することを強制します。
// throwsで宣言する必要がある
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path);
}
// またはキャッチする
public void readFileSafe(String path) {
try {
FileReader reader = new FileReader(path);
} catch (IOException e) {
System.out.println("File error: " + e.getMessage());
}
}非検査例外は、RuntimeExceptionを継承します。コンパイラは、これらを処理することを要求しません。これらは通常、プログラミング上のエラーを示します。
// throws宣言は不要
public int divide(int a, int b) {
return a / b; // ArithmeticException(非検査例外)を投げる可能性がある
}
String name = null;
name.length(); // NullPointerException(非検査例外)カスタム例外を作成する際は、呼び出し側がどのように処理すべきかに基づいて親クラスを選択します。
- 呼び出し側が予期すべき回復可能な条件には、検査例外(
Exceptionを継承)を使用します。 - 正しいコードでは発生してはならないプログラミングエラーには、非検査例外(
RuntimeExceptionを継承)を使用します。
自分で試してみよう
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int age = scanner.nextInt();
// TODO: チェック例外メソッドをテストする
// 出力: "=== Checked Exception Method ==="
// try-catch内で AgeVerifier.verifyAge(age) を呼び出す
// - InvalidAgeExceptionをキャッチ: "Checked exception caught: [message]" を出力
// - NegativeAgeExceptionをキャッチ: "Unchecked exception caught: [message]" を出力
// TODO: 空行を出力し、その後に "=== Unchecked Exception Method ===" を出力
// try-catch内で AgeVerifier.verifyAgeUncheckedOnly(age) を呼び出す
// - NegativeAgeExceptionをキャッチ: "Runtime exception caught: [message]" を出力
// - IllegalArgumentExceptionをキャッチ: "Illegal argument caught: [message]" を出力
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4継承
継承の基本 (extends)super キーワードメソッドのオーバーライド (@Override)コンストラクタチェーンObject クラス単一継承と多段階継承なぜ多重継承ができないのかまとめ:従業員の階層構造