コンテンツにスキップ

Java/基礎/例外処理

出典: フリー教科書『ウィキブックス(Wikibooks)』

例外処理

[編集]

プログラミングにおける例外処理は、プログラムが実行中に発生する予期しないエラーや異常な状況に対処するための仕組みや手法を指します。プログラムが実行中にエラーが発生する可能性がある場合、例外処理はプログラムの安定性を維持し、クラッシュや異常終了を防ぎます。

以下は、プログラミングにおける例外処理の基本的な概念です:

  1. 例外の発生: プログラムが実行中にエラーが発生すると、通常のプログラムのフローが中断されます。このような状況を例外と呼びます。例外は、ゼロ除算、配列の範囲外アクセス、ファイルが見つからないなどのさまざまな条件で発生します。
  2. 例外のスロー: プログラム内の特定の箇所で、例外が発生したことを明示的に示すことができます。この動作を例外のスローと呼びます。通常、特定の条件が満たされた場合やエラーが発生した場合に、例外をスローします。
  3. 例外のキャッチ: プログラム内で例外がスローされた場合、適切な処理を行うために例外をキャッチすることができます。この動作を例外のキャッチと呼びます。例外をキャッチすることで、プログラムは正常に処理を継続するか、エラーを適切に通知することができます。
  4. 例外ハンドリング: 例外をキャッチして処理する手法を例外ハンドリングと呼びます。例外ハンドリングでは、例外をキャッチし、エラーをログに記録したり、ユーザーにエラーメッセージを表示したり、プログラムの状態を回復させたりすることができます。

例外処理は、プログラミングにおいて非常に重要です。適切に実装された例外処理は、プログラムの安定性を高め、ユーザーエクスペリエンスを向上させるのに役立ちます。また、例外処理はデバッグや問題解決の際にも役立ちます。

Javaの例外処理

[編集]

Javaの例外処理は、プログラム実行中に予期しない状況やエラーが発生した場合に、その状況を適切に処理するための仕組みです。Javaの例外処理は、プログラムの安全性や信頼性を高めるために非常に重要です。

例外は、実行時に発生するエラーの種類や条件を表します。例えば、ゼロ除算、配列の範囲外へのアクセス、ファイルが見つからないなどのエラーは、Javaの例外処理を使用して適切に処理することができます。

Javaの例外処理は以下のような特徴を持ちます:

  1. 例外クラスの階層構造: Javaでは、Throwable クラスを基底クラスとして、例外を表すさまざまなクラスが階層的に定義されています。Throwable クラスのサブクラスには、Exception(検査例外)や RuntimeException(非検査例外)などがあります。
  2. try-catch-finally ブロック: Javaでは、try ブロック内で例外が発生する可能性のあるコードを囲み、それに対する処理を catch ブロックで定義します。また、finally ブロックを使用して、例外の発生にかかわらず必ず実行される処理を記述することができます。
  3. 例外のスロー: メソッド内で発生した例外を明示的に処理せずに、呼び出し元に例外をスローすることができます。これにより、例外を適切な場所で処理することができます。
  4. 検査例外と非検査例外: Javaでは、検査例外(checked exception)と非検査例外(unchecked exception)の2種類の例外があります。検査例外はコンパイル時にチェックされるため、明示的に処理するか、メソッドの throws 宣言で伝播させる必要があります。一方、非検査例外は実行時に発生し、明示的な処理が必要ありません。

Javaの例外処理は、プログラムのロバストさを向上させ、予期しない状況に対処するための重要な手段です。例外処理を適切に使用することで、プログラムの安全性や信頼性を向上させることができます。

以下は、Javaの例外処理を1つのソースコードで解説したものです。

public class ExceptionHandlingQuickTour {
    public static void main(String[] args) {
        try {
            // 例外が発生する可能性のあるコード
            int result = divide(10, 0);
            System.out.println("結果: " + result); // この行は実行されません
        } catch (ArithmeticException e) {
            // ArithmeticException が発生した場合の処理
            System.out.println("0 で割ることはできません。");
        } finally {
            // 必ず実行されるブロック
            System.out.println("プログラムの実行が完了しました。");
        }
    }

    // 例外をスローする可能性のあるメソッド
    public static int divide(int num1, int num2) {
        if (num2 == 0) {
            throw new ArithmeticException("0 で割ることはできません。");
        }
        return num1 / num2;
    }
}

この例では、次のような内容を含んでいます:

  1. main メソッド内で try-catch-finally ブロックが使用されています。try ブロック内では例外が発生する可能性のあるコードが配置され、catch ブロックでは特定の例外が発生した場合の処理が定義されています。finally ブロックは例外の発生にかかわらず必ず実行されるブロックです。
  2. divide メソッドは、引数 num2 が 0 の場合に ArithmeticException をスローする可能性があります。
  3. main メソッドでは、divide メソッドを呼び出し、0 で割るエラーが発生した場合に ArithmeticException をキャッチし、適切なメッセージを出力します。
  4. finally ブロックは、プログラムが正常に終了したかどうかに関係なく、必ず実行されることが保証されています。

throws

[編集]

Javaには throws キーワードがあります。throws キーワードは、メソッドが特定の例外をスローする可能性があることを宣言するために使用されます。

メソッドが特定の例外をスローする可能性がある場合、そのメソッドのシグネチャに throws キーワードを使用して、その例外を指定します。これにより、メソッドを呼び出す際に、呼び出し元がその例外を適切に処理するか、またはさらに上位の呼び出し元に例外を伝播させるかを決定できます。

例えば:

public void readFile() throws IOException {
    // ファイルを読み込む処理
}


この例では、readFile メソッドが IOException をスローする可能性があることが宣言されています。メソッド内で IOException が発生する可能性がある場合、その例外をキャッチして処理するか、または readFile メソッドの呼び出し元で try-catch ブロックを使用して例外を処理する必要があります。

throws キーワードを使用することで、メソッドの呼び出し元がそのメソッドがスローする可能性がある例外に対処できるようになります。

例外クラス

[編集]

Javaの例外には、いくつかの主要なクラスがあります。

以下に、Javaの例外クラスのいくつかを表組みで示します。

主要な例外クラス
例外クラス 説明
ArithmeticException 数学的な操作中に発生する例外(ゼロ除算など)
ArrayIndexOutOfBoundsException 配列への無効なインデックスアクセスが発生した場合の例外
NullPointerException ヌル参照が使用された場合の例外
IOException 入出力操作中に発生する例外
FileNotFoundException ファイルが見つからない場合の例外
NumberFormatException 文字列が数値に変換できない場合の例外
IllegalArgumentException メソッドに渡された引数が無効な場合の例外
RuntimeException 実行時に発生する一般的な例外の基底クラス

これらはJavaの例外の一部であり、それぞれ特定の状況や条件で発生します。Javaの例外処理では、これらの例外クラスを適切にキャッチして処理することが重要です。また、必要に応じて独自の例外クラスを定義することもできます。

モダンな例外処理

[編集]

Java 7以降で導入された機能と、最新バージョンでの改善点を説明します:

try-with-resources

[編集]

リソースの自動クローズを行う構文です:

public class ModernExceptionHandling {
    public static void readFile(String path) {
        // try-with-resources: リソースは自動的にクローズされます
        try (var reader = new BufferedReader(new FileReader(path))) {
            String line = reader.readLine();
            System.out.println(line);
        } catch (IOException e) {
            System.err.println("ファイル読み込みエラー: " + e.getMessage());
        }
        // finally不要: readerは自動的にクローズされます
    }
    
    // 複数のリソースを扱う例
    public static void copyFile(String src, String dst) {
        try (var reader = new BufferedReader(new FileReader(src));
             var writer = new BufferedWriter(new FileWriter(dst))) {
            reader.lines().forEach(line -> {
                try {
                    writer.write(line);
                    writer.newLine();
                } catch (IOException e) {
                    throw new UncheckedIOException(e);
                }
            });
        } catch (IOException e) {
            System.err.println("ファイル操作エラー: " + e.getMessage());
        }
    }
}

マルチキャッチと型推論

[編集]

複数の例外を効率的に処理する方法:

public class MultiCatchExample {
    public void processData() {
        try {
            // 複数の例外が発生する可能性がある処理
            readFromFile();
            processNumbers();
        } catch (IOException | NumberFormatException e) {
            // 複数の例外を1つのcatchブロックで処理
            logger.error("データ処理エラー", e);
        }
    }
    
    // Java 17以降の改善された型推論
    public void handleException(Exception e) {
        if (e instanceof IOException || e instanceof SQLException ex) {
            // exの型が自動的に推論される
            logError(ex);
        }
    }
}

改善されたNullPointerException

[編集]

Java 14以降で導入された詳細なNullPointerExceptionメッセージ:

public class ImprovedNPE {
    record Person(String name, Address address) {}
    record Address(String street) {}
    
    public void demonstrateNPE() {
        Person person = new Person("John", null);
        // 以下のコードは詳細なエラーメッセージを生成:
        // "Cannot invoke "ImprovedNPE.Address.street()" because the return value 
        // of "ImprovedNPE.Person.address()" is null"
        String street = person.address().street();
    }
}

カスタム例外の設計

[編集]

モダンなJavaでのカスタム例外の実装例:

public class CustomExceptionExample {
    // カスタム例外クラス
    public sealed abstract class BusinessException 
        extends Exception 
        permits ValidationException, NotFoundException {
        
        private final String errorCode;
        
        protected BusinessException(String message, String errorCode) {
            super(message);
            this.errorCode = errorCode;
        }
        
        public String getErrorCode() {
            return errorCode;
        }
    }
    
    // 具体的な例外クラス
    public final class ValidationException extends BusinessException {
        public ValidationException(String message) {
            super(message, "VAL_001");
        }
    }
    
    public final class NotFoundException extends BusinessException {
        public NotFoundException(String message) {
            super(message, "NOT_001");
        }
    }
}