ホームページ  >  記事  >  Java  >  Java での例外処理: 完全ガイド

Java での例外処理: 完全ガイド

Susan Sarandon
Susan Sarandonオリジナル
2024-11-06 07:17:02792ブラウズ

Gestion des Exceptions en Java : Guide Complet

Java の例外は、堅牢なプログラミングの重要な部分です。実際、組織的かつ予測可能な方法でエラーを管理できるようになります。この記事では、Java の例外処理システムについて詳しく説明し、ベスト プラクティスを提供し、取り上げる概念を説明するために実際の例を示します。

1. 例外の概要

Java では、例外とは、プログラムの実行中に発生し、通常の命令フローを中断する予期しないイベントです。例外により、プログラムはクラッシュせずにエラーを処理できます。

単純なエラーの例: ゼロ除算:

public class DivisionParZero {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        System.out.println(a / b);  // Lève une ArithmeticException
    }
}

2. Java の例外の種類

Java は例外を 3 つの主要なカテゴリに分類します。

2.1.チェックされた例外

これらの例外はキャッチされるか、スローを使用してメソッド シグネチャ内で宣言される必要があります。多くの場合、ファイル アクセスやネットワーク接続などの外部イベントから発生します。

  • IOException: 入出力操作が失敗した場合に発生します。たとえば、存在しないファイルへのアクセス (FileNotFoundException)。
  • SQLException: データベース操作エラー中に発生します。
  • ClassNotFoundException: JVM が指定されたクラスを見つけられない場合にスローされる例外。
  • InstantiationException: 抽象クラスまたはインターフェイスをインスタンス化しようとしたとき。
  • InterruptedException: 待機中のスレッドが中断されたときに発生します。
  • ParseException: データ、特に日付の解析時のエラーが原因で発生します。

2.2.未チェックの例外

未チェックの例外は RuntimeException を継承しており、キャッチしたり宣言したりする必要はありません。多くの場合、プログラミング エラーが原因で発生します。

  • NullPointerException: null 参照を使用しようとしました。
  • ArrayIndexOutOfBoundsException: 無効な配列インデックスへのアクセス。
  • ArithmeticException: ゼロによる除算など、無効な数学演算です。
  • IllegalArgumentException: メソッドで不適切な引数が使用されています。
  • NumberFormatException: 数値以外の文字列を数値に変換します (例: Integer.parseInt("abc"))。
  • IllegalStateException: オブジェクトの不適切な状態でメソッドを呼び出しています。

2.3.エラー

Java のエラーは重大な問題であり、多くの場合、プログラムでは処理できないシステム関連の問題です。

  • OutOfMemoryError: JVM のメモリが不足すると発生します。
  • StackOverflowError: 過剰な再帰がスタック サイズを超えると発生します。
  • VirtualMachineError: InternalError や UnknownError などの重大な JVM エラーの親クラス。
  • AssertionError: アサーションの失敗によって引き起こされたエラー。

その他の既知の特定の例外

  • StringIndexOutOfBoundsException: 文字列内の範囲外の文字位置にアクセスしようとしたとき。
  • ClassCastException: オブジェクトを互換性のない型にキャストしようとしました。
  • UnsupportedOperationException: 現在の実装でサポートされていない操作を呼び出しています。
  • ConcurrentModificationException: 反復中のコレクションの変更。

これらの例外は Java で発生する可能性のある広範囲のエラーをカバーしており、アプリケーション内のさまざまなエラー シナリオを処理するための包括的な基盤を提供します。

3. 例外の処理

Java では、例外処理は主に try、catch、finally、throw ブロックに基づいています。これらの使用法の詳細な概要は次のとおりです:

ブロックを試してキャッチしてください

try ブロックは、例外を生成する可能性のあるコードをカプセル化します。例外が発生した場合、対応する catch ブロックが実行されてこの例外をキャッチして処理し、プログラムのクラッシュを防ぎます。

例:
文字列を整数に変換したいが、その文字列が有効な数値ではない可能性があるというシナリオを想像してみましょう。

public class DivisionParZero {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        System.out.println(a / b);  // Lève une ArithmeticException
    }
}

この例では、「abc」などの整数に変換できない文字列をユーザーが入力すると、catch ブロックが NumberFormatException をキャッチしてエラー メッセージを表示し、プログラムの中断を回避します。

最後にブロックする

finally ブロックは、例外がスローされたかどうかに関係なく、try ブロックと catch ブロックの後に実行されます。これは、リソースが常に適切にクリーンアップされるようにするために、リソースを解放する (ファイルやネットワーク接続を閉じるなど) ためによく使用されます。

例:

public class GestionDesExceptions {
    public static void main(String[] args) {
        try {
            int number = Integer.parseInt("abc"); // Provoquera une NumberFormatException
        } catch (NumberFormatException e) {
            System.out.println("Erreur : la chaîne de caractères n'est pas un nombre valide.");
        }
    }
}

ここで、finally ブロックはファイルが見つかったかどうかに関係なくファイルを閉じ、リソースを解放します。

キーワードを投げる

throw キーワードは、明示的に例外をスローするために使用されます。これは、コード内の特定の検証に役立ちます。

例:

public class GestionDesExceptions {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("fichier.txt");
            // Lire le fichier
        } catch (FileNotFoundException e) {
            System.out.println("Erreur : fichier non trouvé.");
        } finally {
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    System.out.println("Erreur lors de la fermeture du fichier.");
                }
            }
        }
    }
}

この例では、年齢が負の場合、 throw は明示的に IllegalArgumentException をスローするため、論理エラーを示します。

4. 例外の伝播

特に現在のメソッドが例外を適切に処理できない場合は、例外をすぐにキャッチするのではなく、伝播した方がよい場合があります。これは、メソッド宣言内の throws キーワードを使用して行われます。

例:

public class GestionDesExceptions {
    public static void main(String[] args) {
        try {
            validerAge(-5); 
        } catch (IllegalArgumentException e) {
            System.out.println("Erreur : " + e.getMessage());
        }
    }

    public static void validerAge(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("L'âge ne peut pas être négatif.");
        }
    }
}

ここで、openFile は throws IOException を使用します。これは、呼び出し元に例外を処理させることを意味します。 FileReader がファイルを見つけられない場合、FileNotFoundException がスローされ、メイン メソッドの catch ブロックに渡されます。

この伝播は、呼び出し階層の上位で例外を処理する必要がある、たとえばプレゼンテーション層でユーザーにエラー メッセージを表示する必要がある、より複雑なアーキテクチャで役立ちます。

5. カスタム例外

Java では、Exception クラスまたは RuntimeException クラスを継承してカスタム例外を作成できます。これらの例外は、標準例外ではカバーされないアプリケーション固有のエラーを報告するのに役立ちます。

カスタム例外の例

ユーザーが持っている以上のお金を引き出すことができない銀行アプリを作成するとします。 InsufficientBalanceException を作成します。

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class PropagationException {
    public static void main(String[] args) {
        try {
            ouvrirFichier("fichier_inexistant.txt");
        } catch (IOException e) {
            System.out.println("Erreur lors de l'ouverture du fichier : " + e.getMessage());
        }
    }

    public static void ouvrirFichier(String nomFichier) throws IOException {
        FileReader lecteur = new FileReader(nomFichier); // Peut lever FileNotFoundException
        lecteur.close();
    }
}

次に、それをメイン プログラムで使用して、残高が不十分な状況に対処します。

public class SoldeInsuffisantException extends Exception {
    public SoldeInsuffisantException(String message) {
        super(message);
    }
}

この例では、InsufficientBalanceException は、引き出し金額が利用可能な残高を超えていることを示すカスタム例外です。このエラーを処理すると、明示的なエラー メッセージが提供されるため、コードの可読性と保守性が特に向上します。

ユーザー検証のカスタム例外の例

年齢が 18 歳から 65 歳までである必要があるユーザー認証システムを想像してみましょう。 AgeInvalideException を作成できます。

public class DivisionParZero {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        System.out.println(a / b);  // Lève une ArithmeticException
    }
}

使用法:

public class GestionDesExceptions {
    public static void main(String[] args) {
        try {
            int number = Integer.parseInt("abc"); // Provoquera une NumberFormatException
        } catch (NumberFormatException e) {
            System.out.println("Erreur : la chaîne de caractères n'est pas un nombre valide.");
        }
    }
}

ここでは、年齢が条件を満たさない場合に AgeInvalideException がスローされます。このカスタム例外により、正確な検証チェックと明確なエラー メッセージが提供され、ユーザー エクスペリエンスが向上します。

try、catch、finally、およびカスタム例外による Java の例外処理により、きめ細かいエラー制御、コードの可読性の向上、プロフェッショナル アプリケーションでのユーザー エクスペリエンスの向上が可能になります。

6. 例外処理のベストプラクティス

6.1 意味のある例外の使用

原則: 例外メッセージは、エラーに関する正確なコンテキストを提供するために明示的である必要があります。

悪い習慣:

public class GestionDesExceptions {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("fichier.txt");
            // Lire le fichier
        } catch (FileNotFoundException e) {
            System.out.println("Erreur : fichier non trouvé.");
        } finally {
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    System.out.println("Erreur lors de la fermeture du fichier.");
                }
            }
        }
    }
}

改善点:
特定の例外を使用し、メッセージをクリアします。一般的なドメイン固有のエラーを反映する場合は、カスタム例外を作成することを推奨します。

public class GestionDesExceptions {
    public static void main(String[] args) {
        try {
            validerAge(-5); 
        } catch (IllegalArgumentException e) {
            System.out.println("Erreur : " + e.getMessage());
        }
    }

    public static void validerAge(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("L'âge ne peut pas être négatif.");
        }
    }
}

6.2 フロー制御の例外の回避

例外は、if-else などの制御構造をオーバーライドしてはなりません。この方法で例外を使用すると、パフォーマンスの面でコストがかかり、コードが読みにくくなります。

悪い習慣:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class PropagationException {
    public static void main(String[] args) {
        try {
            ouvrirFichier("fichier_inexistant.txt");
        } catch (IOException e) {
            System.out.println("Erreur lors de l'ouverture du fichier : " + e.getMessage());
        }
    }

    public static void ouvrirFichier(String nomFichier) throws IOException {
        FileReader lecteur = new FileReader(nomFichier); // Peut lever FileNotFoundException
        lecteur.close();
    }
}

改善点: このタイプのロジックを処理するには、通常の制御構造を使用します。

public class SoldeInsuffisantException extends Exception {
    public SoldeInsuffisantException(String message) {
        super(message);
    }
}

6.3 クローズされたリソースに対する try-with-resources の使用

try-with-resources ブロックは、例外が発生した場合でも、ファイルや接続などのリソースが自動的に閉じられるようにします。

悪い習慣:

public class CompteBancaire {
    private double solde;

    public CompteBancaire(double solde) {
        this.solde = solde;
    }

    public void retirer(double montant) throws SoldeInsuffisantException {
        if (montant > solde) {
            throw new SoldeInsuffisantException("Solde insuffisant pour ce retrait.");
        }
        solde -= montant;
    }

    public static void main(String[] args) {
        CompteBancaire compte = new CompteBancaire(100.0);

        try {
            compte.retirer(150.0);
        } catch (SoldeInsuffisantException e) {
            System.out.println(e.getMessage());
        }
    }
}

改善点:

public class AgeInvalideException extends Exception {
    public AgeInvalideException(String message) {
        super(message);
    }
}

6.4 一般例外をキャッチしない

重大なエラーや予期しないバグが隠れてしまう可能性があるため、Exception や Throwable などの一般的な例外をキャッチしないでください。特定の例外をキャッチすると、コードが読みやすく、保守しやすくなります。

悪い習慣:

public class ValidationUtilisateur {
    public static void validerAge(int age) throws AgeInvalideException {
        if (age < 18 || age > 65) {
            throw new AgeInvalideException("Âge invalide : doit être entre 18 et 65 ans.");
        }
    }

    public static void main(String[] args) {
        try {
            validerAge(17); 
        } catch (AgeInvalideException e) {
            System.out.println(e.getMessage());
        }
    }
}

改善:より正確なエラー処理のために特定の例外をターゲットにします。

public class DivisionParZero {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        System.out.println(a / b);  // Lève une ArithmeticException
    }
}

6.5 例外のログ記録

例外ログにより、問題の追跡と解決が容易になります。 Log4j や SLF4J などのログ フレームワークを使用してエラーをログに記録し、適切なログ レベル (エラー、警告、情報) を選択します。

悪い習慣:

public class GestionDesExceptions {
    public static void main(String[] args) {
        try {
            int number = Integer.parseInt("abc"); // Provoquera une NumberFormatException
        } catch (NumberFormatException e) {
            System.out.println("Erreur : la chaîne de caractères n'est pas un nombre valide.");
        }
    }
}

改善点:

public class GestionDesExceptions {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("fichier.txt");
            // Lire le fichier
        } catch (FileNotFoundException e) {
            System.out.println("Erreur : fichier non trouvé.");
        } finally {
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    System.out.println("Erreur lors de la fermeture du fichier.");
                }
            }
        }
    }
}

7. 高度な概念

7.1 非同期環境における例外

CompletableFuture などの非同期環境では、メインの実行フローの外側でエラーが発生する可能性があるため、例外処理がより複雑になります。

例:

public class GestionDesExceptions {
    public static void main(String[] args) {
        try {
            validerAge(-5); 
        } catch (IllegalArgumentException e) {
            System.out.println("Erreur : " + e.getMessage());
        }
    }

    public static void validerAge(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("L'âge ne peut pas être négatif.");
        }
    }
}

7.2 例外のエンベロープ化

初期コンテキストを保持しながら例外を再スローするには、getCause() を使用します。これは、アプリケーションの上位層で例外を処理する場合に特に便利です。

例:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class PropagationException {
    public static void main(String[] args) {
        try {
            ouvrirFichier("fichier_inexistant.txt");
        } catch (IOException e) {
            System.out.println("Erreur lors de l'ouverture du fichier : " + e.getMessage());
        }
    }

    public static void ouvrirFichier(String nomFichier) throws IOException {
        FileReader lecteur = new FileReader(nomFichier); // Peut lever FileNotFoundException
        lecteur.close();
    }
}

この例では、e が初期原因であり、追跡を容易にするために getCause() によって取得できます。


8. 単体テストと例外処理

単体テストは、例外が適切にスローされ処理されることを保証します。 JUnit を使用すると、メソッドが予期した例外をスローするかどうかを確認できます。

例:

public class SoldeInsuffisantException extends Exception {
    public SoldeInsuffisantException(String message) {
        super(message);
    }
}

この例では、assertThrows は、ゼロ除算の場合に除算が ArithmeticException をスローすることをチェックします。

例外処理に関するこれらのベスト プラクティスに従うことで、Java コードをより堅牢で保守しやすくすることができます。適切なエラー処理は、アプリケーションの安定性を保証するだけでなく、エラーの追跡可能性も向上させ、デバッグと継続的な改善を容易にします。

結論

要約すると、Java での厳密な例外処理により、コードの信頼性と保守性が強化されます。明示的なエラー メッセージ、try-with-resource の賢明な使用、例外の読みやすさとテストのしやすさへの配慮などのベスト プラクティスを採用することで、不必要な中断を回避し、より安定したユーザー エクスペリエンスを保証します。これにより、進化に備えた堅牢なコード ベースを提供しながら、エラーを効率的に検出、理解、修正することが可能になります。

以上がJava での例外処理: 完全ガイドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。