例外処理の基本 try-catch

docs

Java学習、順調に進んでいますか?以前、条件分岐繰り返し処理について触れましたね。

【Java初心者向け】もしも〜なら!if文でプログラムを賢く動かそう! | ToolDocs

Java超入門!「for文」で処理を繰り返しまくろう! | ToolDocs


プログラムはこれで書けるようになったものの、実際に動かすと予期せぬエラーで止まってしまうことはありませんか?

今回は、そんなプログラムの「困った!」を解決する例外処理について解説します。特に、基本中の基本である**try-catch**構文を徹底的に見ていきましょう。これをマスターすれば、あなたのプログラムはもっと強く、もっと賢くなりますよ。

プログラムを書いていると、意図しない問題が発生して処理が中断してしまうことがあります。例えば、ファイルを読み込もうとしたのにファイルが見つからなかったり、数値をゼロで割ってしまったり、といったケースです。これらをJavaでは**「例外(Exception)」**と呼びます。

例外が発生すると、何も対策していないプログラムはそこで「ガシャーン!」と止まってしまいます。ユーザーから見れば、アプリが突然落ちたように見えてしまいますよね。これを防ぎ、問題が起きてもプログラムを安全に動かし続けるための仕組みが例外処理です。

そして、その中心となるのが**try-catch**構文です。

try-catchって何?

try-catchは、「もしかしたらエラーが起きるかもしれない処理」をtryブロックの中に書き、「もしエラーが起きたら、こんな風に対応してね」という処理をcatchブロックの中に書く、というものです。

簡単に言うと、

  • try: ここで危ない処理を試してみるよ!
  • catch: もし危ない処理でエラーが起きたら、ここで助けてあげるね!

という役割分担です。


基本の書き方

まずは、try-catchの基本的な形を見てみましょう。

Java

try {
    // 例外が発生する可能性がある処理
    // ここで何かエラーが起こるかも?
} catch (例外の型 変数名) {
    // 例外が発生した場合の処理
    // エラーが起きたらここでリカバリー!
}

「例外の型」というのは、発生する可能性のあるエラーの種類のことです。Javaにはたくさんの例外の型がありますが、最初は一般的なものから覚えていきましょう。


例1:ゼロで割る(算術演算例外)

プログラミングでは、どんな数でもゼロで割ることはできません。もしゼロで割ろうとすると、「ArithmeticException(算術演算例外)」という例外が発生します。

Java

public class DivisionExample {
    public static void main(String[] args) {
        int numerator = 10;
        int denominator = 0; // ゼロで割ろうとしています!

        try {
            int result = numerator / denominator; // ここでArithmeticExceptionが発生する可能性あり
            System.out.println("計算結果: " + result); // 例外が発生するとここは実行されない
        } catch (ArithmeticException e) {
            // ArithmeticExceptionが発生した場合の処理
            System.err.println("エラー: ゼロで割ることはできません!");
            System.err.println("詳細: " + e.getMessage()); // エラーメッセージを表示
        }
        System.out.println("プログラムは終了しました。"); // 例外処理のおかげでここまで実行される!
    }
}

実行結果:

エラー: ゼロで割ることはできません!
詳細: / by zero
プログラムは終了しました。

tryブロック内でdenominator0なのでnumerator / denominatorのところでArithmeticExceptionが発生します。すると、その後のSystem.out.println("計算結果: " + result);は実行されず、すぐにcatch (ArithmeticException e)ブロックの中の処理が実行されます。

例外処理がない場合、プログラムはnumerator / denominator;の行で強制終了してしまいますが、try-catchを使うことで、エラーメッセージを表示しつつも、最後のSystem.out.println("プログラムは終了しました。");まで実行されるのが分かりますね。


paizaで実行した結果

例2:不正な数値変換(数値形式例外)

文字列を数値に変換しようとしたとき、もしその文字列が正しい数値の形をしていなかったらどうなるでしょう?例えば、「abc」という文字列を整数に変換しようとすると、「NumberFormatException(数値形式例外)」が発生します。

Java

public class NumberConversionExample {
    public static void main(String[] args) {
        String strNumber = "123";
        String invalidStrNumber = "abc"; // 数値に変換できない文字列

        // 正常な場合
        try {
            int num1 = Integer.parseInt(strNumber);
            System.out.println("変換成功 (1): " + num1);
        } catch (NumberFormatException e) {
            System.err.println("エラー (1): 数値に変換できませんでした。");
        }

        System.out.println("---");

        // 異常な場合
        try {
            int num2 = Integer.parseInt(invalidStrNumber); // ここでNumberFormatExceptionが発生する可能性あり
            System.out.println("変換成功 (2): " + num2); // 例外が発生するとここは実行されない
        } catch (NumberFormatException e) {
            // NumberFormatExceptionが発生した場合の処理
            System.err.println("エラー (2): 不正な数値形式です!");
            System.err.println("詳細: " + e.getMessage());
        }
        System.out.println("変換処理は終了しました。");
    }
}

実行結果:

変換成功 (1): 123
---
エラー (2): 不正な数値形式です!
詳細: For input string: "abc"
変換処理は終了しました。

"123"は問題なく変換されますが、"abc"を変換しようとするとNumberFormatExceptionが発生し、catchブロックの処理が実行されます。ここでも、プログラムは途中で止まらずに最後まで実行されていますね。

paizaで実行した結果


例3:配列の範囲外アクセス(配列インデックス例外)

配列を使っていると、指定したインデックス(要素番号)が配列の範囲を超えてしまうことがあります。例えば、要素が3つしかない配列の4番目の要素にアクセスしようとすると、「ArrayIndexOutOfBoundsException(配列インデックス範囲外例外)」が発生します。

Java

public class ArrayAccessExample {
    public static void main(String[] args) {
        String[] fruits = {"Apple", "Banana", "Orange"}; // 要素は0, 1, 2の3つ

        try {
            System.out.println("果物 (0番目): " + fruits[0]); // 正常なアクセス
            System.out.println("果物 (1番目): " + fruits[1]); // 正常なアクセス
            System.out.println("果物 (3番目): " + fruits[3]); // ここでArrayIndexOutOfBoundsExceptionが発生する可能性あり
            System.out.println("果物 (2番目): " + fruits[2]); // 例外が発生するとここは実行されない
        } catch (ArrayIndexOutOfBoundsException e) {
            // ArrayIndexOutOfBoundsExceptionが発生した場合の処理
            System.err.println("エラー: 配列の範囲を超えています!");
            System.err.println("配列の要素数: " + fruits.length);
            System.err.println("詳細: " + e.getMessage());
        }
        System.out.println("配列へのアクセス処理は終了しました。");
    }
}

実行結果:

果物 (0番目): Apple
果物 (1番目): Banana
エラー: 配列の範囲を超えています!
配列の要素数: 3
詳細: Index 3 out of bounds for length 3
配列へのアクセス処理は終了しました。

fruits[3]にアクセスしようとした瞬間にArrayIndexOutOfBoundsExceptionが発生し、catchブロックの処理が実行されます。これもtry-catchがなければ、プログラムはそこで強制終了です。

paizaで実行した結果


複数のcatchブロック

一つのtryブロックに対して、複数のcatchブロックを記述することも可能です。これは、様々な種類の例外が発生する可能性がある場合に便利です。

Java

import java.util.Scanner;

public class MultiCatchExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        try {
            System.out.print("1つ目の数値を入力してください: ");
            String str1 = scanner.nextLine();
            int num1 = Integer.parseInt(str1);

            System.out.print("2つ目の数値を入力してください: ");
            String str2 = scanner.nextLine();
            int num2 = Integer.parseInt(str2);

            int result = num1 / num2;
            System.out.println("計算結果: " + result);

        } catch (NumberFormatException e) {
            System.err.println("エラー: 無効な数値が入力されました!");
            System.err.println("詳細: " + e.getMessage());
        } catch (ArithmeticException e) {
            System.err.println("エラー: ゼロで割ることはできません!");
            System.err.println("詳細: " + e.getMessage());
        } catch (Exception e) { // その他の例外をまとめてキャッチ
            System.err.println("予期せぬエラーが発生しました。");
            System.err.println("詳細: " + e.getMessage());
        } finally {
            // tryブロックとcatchブロックのどちらが実行されても、最後に必ず実行される処理
            System.out.println("--- 処理を終了します ---");
            scanner.close(); // Scannerは使い終わったら閉じるのがお約束
        }
    }
}

この例では、

  1. NumberFormatException(数値に変換できない文字列が入力された場合)
  2. ArithmeticException(2つ目の数値が0で割ろうとした場合)

の2種類の例外をそれぞれ別のcatchブロックで処理しています。

また、最後のcatch (Exception e)は、上記2つの例外以外のすべての例外をキャッチすることができます。これは「とりあえず何かエラーが起きたらこれで受け止める」という、いわば「万能のcatch」ですが、できるだけ具体的な例外の型を指定する方が、より適切なエラーハンドリングができるのでおすすめです。

paizaで実行した結果

finallyブロックについて

上の例で出てきたfinallyブロックは、tryブロックとcatchブロックのどちらが実行されたとしても、必ず最後に実行される処理を書く場所です。ファイルのクローズ処理やデータベース接続の切断など、確実に実行したい後処理に利用されます。


例外処理を使うメリット

  • プログラムが落ちない: 予期せぬエラーで強制終了するのを防ぎ、プログラムを安定稼働させられます。
  • ユーザー体験の向上: エラーが発生しても、意味の分からないエラーメッセージではなく、ユーザーに分かりやすいメッセージを表示したり、代替処理を行ったりできます。
  • 堅牢なプログラム: どんな状況でも耐えうる、頑丈なプログラムを作ることができます。

まとめ

今回は、Javaプログラミングにおけるエラー対策の要、**例外処理の基本 try-catch**について学びました。

  • tryブロック: エラーが起こるかもしれない処理を試す場所
  • catchブロック: エラーが起きたときに、どう対応するかを書く場所

このtry-catchを使うことで、あなたのプログラムは突然止まることなく、問題が発生しても適切に対応できるようになります。まるで、エラーが発生したときに自動で緊急ブレーキをかけ、冷静に状況を知らせてくれる車のようですね!

これであなたのJavaプログラムは、より実用的で信頼性の高いものになったはずです。 

コメント

タイトルとURLをコピーしました