Java/基礎/例外処理
例外処理
[編集]プログラミングにおける例外処理は、プログラムが実行中に発生する予期しないエラーや異常な状況に対処するための仕組みや手法を指します。プログラムが実行中にエラーが発生する可能性がある場合、例外処理はプログラムの安定性を維持し、クラッシュや異常終了を防ぎます。
以下は、プログラミングにおける例外処理の基本的な概念です:
- 例外の発生: プログラムが実行中にエラーが発生すると、通常のプログラムのフローが中断されます。このような状況を例外と呼びます。例外は、ゼロ除算、配列の範囲外アクセス、ファイルが見つからないなどのさまざまな条件で発生します。
- 例外のスロー: プログラム内の特定の箇所で、例外が発生したことを明示的に示すことができます。この動作を例外のスローと呼びます。通常、特定の条件が満たされた場合やエラーが発生した場合に、例外をスローします。
- 例外のキャッチ: プログラム内で例外がスローされた場合、適切な処理を行うために例外をキャッチすることができます。この動作を例外のキャッチと呼びます。例外をキャッチすることで、プログラムは正常に処理を継続するか、エラーを適切に通知することができます。
- 例外ハンドリング: 例外をキャッチして処理する手法を例外ハンドリングと呼びます。例外ハンドリングでは、例外をキャッチし、エラーをログに記録したり、ユーザーにエラーメッセージを表示したり、プログラムの状態を回復させたりすることができます。
例外処理は、プログラミングにおいて非常に重要です。適切に実装された例外処理は、プログラムの安定性を高め、ユーザーエクスペリエンスを向上させるのに役立ちます。また、例外処理はデバッグや問題解決の際にも役立ちます。
Javaの例外処理
[編集]Javaの例外処理は、プログラム実行中に予期しない状況やエラーが発生した場合に、その状況を適切に処理するための仕組みです。Javaの例外処理は、プログラムの安全性や信頼性を高めるために非常に重要です。
例外は、実行時に発生するエラーの種類や条件を表します。例えば、ゼロ除算、配列の範囲外へのアクセス、ファイルが見つからないなどのエラーは、Javaの例外処理を使用して適切に処理することができます。
Javaの例外処理は以下のような特徴を持ちます:
- 例外クラスの階層構造: Javaでは、
Throwable
クラスを基底クラスとして、例外を表すさまざまなクラスが階層的に定義されています。Throwable
クラスのサブクラスには、Exception
(検査例外)やRuntimeException
(非検査例外)などがあります。 - try-catch-finally ブロック: Javaでは、
try
ブロック内で例外が発生する可能性のあるコードを囲み、それに対する処理をcatch
ブロックで定義します。また、finally
ブロックを使用して、例外の発生にかかわらず必ず実行される処理を記述することができます。 - 例外のスロー: メソッド内で発生した例外を明示的に処理せずに、呼び出し元に例外をスローすることができます。これにより、例外を適切な場所で処理することができます。
- 検査例外と非検査例外: 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; } }
この例では、次のような内容を含んでいます:
main
メソッド内でtry-catch-finally
ブロックが使用されています。try
ブロック内では例外が発生する可能性のあるコードが配置され、catch
ブロックでは特定の例外が発生した場合の処理が定義されています。finally
ブロックは例外の発生にかかわらず必ず実行されるブロックです。divide
メソッドは、引数num2
が 0 の場合にArithmeticException
をスローする可能性があります。main
メソッドでは、divide
メソッドを呼び出し、0 で割るエラーが発生した場合にArithmeticException
をキャッチし、適切なメッセージを出力します。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"); } } }