30. エラーハンドリング

エラーハンドリングとは

エラーハンドリングの意味

プログラム実行中にエラーが発生すると、通常のプログラムは異常終了してしまいます。プログラムを継続的に実行するには、例外処理で回避しなければいけませんが、これをエラーハンドリングといいます。

エラーハンドリングの種類

エラーハンドリング(例外処理) をするとき、以下の3つの構文があります。

  • try-catch
  • throw
  • throws

try-catch

try-catchの基本構文

try-catch文は、tryブロックとcatchブロックで構成されます。

  • tryブロックには例外が発生する可能性のある処理
  • catchブロックに例外が発生したときの処理
  • catch*の引数には例外クラスを指定
try {
    //例外発生の可能性がある処理
} catch (例外クラス インスタンス) {
    //例外処理が発生したときの処理
}

複数の例外catch

try-catch文は複数のcatchブロックを記述できます。

try {
    //例外発生の可能性がある処理
} catch (例外クラス1 インスタンス1) {
    // 対策処理1
} catch (例外クラス2 インスタンス2) {
    // 対策処理2
} finally {
    // 例外に関わらず必ず実行する処理
}

finally

finallyは例外が発生しなくても必ず最後に処理されるブロックです。

try {
    //例外発生の可能性がある処理
} catch (例外クラス1 インスタンス1) {
    // 対策処理1
} catch (例外クラス2 インスタンス2) {
    // 対策処理2
} finally {
    // 例外に関わらず必ず実行する処理
}

外部ファイル読み込み

ファイル構成

./
└── data/
  └── sample.txt
└── src/
  └── sample/
    └── FileLoadApp.java

テキストファイルの準備

「data/sample.txt」にテキストを記述しておきます。

sample.txt
Hello
こんにちは

FileInputStream

FileInputStreamは、外部ファイルを開くためのクラスです。

FileInputStream file = new FileInputStream(ファイルパス);

loadFile() メソッドで、指定されたパスからファイルを読み込んでみます。

ExceptionApp.java
	public static void loadFile(String path) {
		FileInputStream file = new FileInputStream(path);
	}

try-catchで例外処理

FileInputStreamインスタンスを作成をすると、コンパイルエラーになります。これはファイルが読み込まれない可能性があるからです。

クイックフィックスでtry-catch文を追加して、FileInputStreamの処理を囲みます。

try-catchを追加したら、通常処理と例外処理を記述しましょう。

FileLoadApp.java
public static void loadFile(String path) {
    // try-catch の追加
    try {
        FileInputStream file = new FileInputStream(path);
        System.out.println("ファイルを開きました");
    } catch (FileNotFoundException e) {
        // FileNotFoundException の追加
        System.out.println("ファイルが開けませんでした");
    }
}

動作確認

loadFile() にファイルパスを指定して、ファイルが読み込めるか確認してみましょう。

FileLoadApp.java
	public static void main(String[] args) {
		loadFile("./data/sample.txt");
	}

ファイルが存在すれば、ファイルが読み込まれます。

結果
ファイルを開きました

InputStreamReader

InputStreamReaderの基本

InputStreamReaderクラスは、ファイルを読み込むためのクラスです。第2引数には文字エンコードを指定できます。

InputStreamReader reader = new InputStreamReader(リソース, 文字エンコード);

作成したInputStreamReaderで処理が終わったら閉じます。

reader.close();

InputStreamReaderで読み込み準備

FileInputStreamから、InputStreamReaderを作成します。

ExceptionApp.java
    public static void loadFile(String path) {
        try {
            FileInputStream file = new FileInputStream(path);
            InputStreamReader reader = new InputStreamReader(file, "UTF-8");
            System.out.println("ファイルを開きました");
            reader.close();
        } catch (FileNotFoundException e) {
            System.out.println("ファイルが開けませんでした");
        } catch (IOException e) {
            System.out.println("データエラー");
        }
    }
  • 例外処理が必要なため、コンパイルエラーとなります。
  • Windowsでは第2引数に文字エンコードを指定して読み込みます。
  • InputStreamReaderでは、文字エンコードの例外処理が必須です。

BufferedReaderクラス

BufferedReaderの基本

BufferedReaderクラスは、InputStreamReaderからデータを行単位で読み込むことができます。

BufferedReader buffer = new BufferedReader(reader);

作成したBufferedReaderで処理が終わったら閉じます。

buffer.close();

BufferedReaderでデータ読み込み

while文で外部ファイルデータを1行ずつ読み込みます。

FileLoadApp.java
    public static void loadFile(String path) {
        try {
            FileInputStream file = new FileInputStream(path);
            InputStreamReader reader = new InputStreamReader(file, "UTF-8");

            System.out.println("ファイルを開きました");

            BufferedReader buffer = new BufferedReader(reader);
            String line;
            //1行ずつ繰り返し読み込み
            while ((line = buffer.readLine()) != null) {
                System.out.println(line);
            }
            buffer.close();
            reader.close();
        } catch (FileNotFoundException e) {
            System.out.println("ファイルが開けませんでした");
        } catch (IOException e) {
            System.out.println("データエラー");
        }
    }

readLine() では例外処理が必須のため、現状ではコンパイルエラーになります。

IOExceptionのcatch

クイックフィックスで、例外を catch します。

IOExceptionクラスの catchブロックが追加されました。

FileLoadApp.java
	public static void loadFile(String path) {
		try {
			FileInputStream file = new FileInputStream(path);
			InputStreamReader reader = new FileInputStream(file, "UTF-8");
			System.out.println("ファイルを開きました");

			BufferedReader buffer = new BufferedReader(reader);
			String line;
			while ((line = buffer.readLine()) != null) {
				System.out.println(line);
			}
			buffer.close();
			reader.close();
		} catch (FileNotFoundException e) {
			System.out.println("ファイルが開けませんでした");
		} catch (IOException e) {
			// IOException の追加
			System.out.println("ファイルエラー");
		}
	}

finallyの追加

finallyブロックを追加して、共通処理を記述します。

FileLoadApp.java
	public static void loadFile(String path) {
		try {
			...
		} catch (FileNotFoundException e) {
			System.out.println("ファイルが開けませんでした");
		} catch (IOException e) {
			System.out.println("ファイルエラー");
		} finally {
			System.out.println("ファイル読み込み処理を終了します");
		}
	}

動作確認

プログラムを実行して「data/sample.txt」ファイルのテキスト内容が表示できるか確認してみましょう。

結果(ファイルがある場合)
ファイルを開きました
Hello
こんにちは
ファイル読み込み処理を終了します。
結果(ファイルがない場合)
ファイルが開けませんでした
ファイル読み込み処理を終了します

throws

throwsとは

throwsは例外が発生したときに、呼び出し元に例外処理を投げる方法です。

	public メソッド() throws 例外 {
		//例外の可能性のある処理
	}

ファイル読み込み(throws)

loadFile2() メソッドで同じようにファイル読み込み処理を作成します。

FileLoadApp.java
	public static void loadFile2(String path)  {
		FileReader reader = new FileReader(path);
		System.out.println("ファイルを読み込みました");

		BufferedReader buffer = new BufferedReader(reader);
		String line;
		while ((line = buffer.readLine()) != null) {
			System.out.println(line);
		}
		buffer.close();
		reader.close();
	}

throwsの追加

readLine() を選択し、クイックフィックスでthrowsを追加します。

メソッドに throws IOExceptionが追加されました。

FileLoadApp.java
	public static void loadFile2(String path) throws IOException {
		FileReader reader = new FileReader(path);
		System.out.println("ファイルを読み込みました");

		BufferedReader buffer = new BufferedReader(reader);
		String line;
		while ((line = buffer.readLine()) != null) {
			System.out.println(line);
		}
		buffer.close();
		reader.close();
	}

呼び出し元で「try-catch」

コンパイルエラー

loadFile2() を実行するとコンパイルエラーになります。

FileLoadApp.java
	public static void main(String[] args) {
		...

		loadFile2("./data/sample.txt");
	}

try-catchの追加

呼び出し元で「try-catch」文を追加します。

try-catch文が作成されたら、通常処理と例外処理を記述しましょう。

FileLoadApp.java
	public static void main(String[] args) {
		...

		try {
			loadFile2("./data/sample.txt");
		} catch (IOException e) {
			System.out.println("ファイルエラー");
		}
	}

動作確認

プログラムを実行して動作確認してみましょう。

結果
Hello
こんにちは

throw

throwは、意図的に例外を発生させて処理します。例えば値が想定と違う場合、意図的に例外を発生させ、エラーメッセージを返すことができます。

throw new Exceptionクラス(値);

throwの例

countがマイナスの数値と判別したら、IllegalArgumentExceptionを発生させて、catchで処理します。

ExceptionApp.java
    public static void main(String[] args) {
        int score = 100;
        int count = -1;
        float average = 0;
        average = calculateAverage(score, count);
        System.out.println(average);
    }

    public static float calculateAverage(int score, int count) {
        // return score / count;
        try {
            if (count < 0) {
                throw new IllegalArgumentException();
            }
        } catch (IllegalArgumentException e) {
            System.out.println("個数がマイナスです。");
            return 0;
        }
        if (count > 0) {
            return (float) score / count;
        } else {
            System.out.println("個数が間違っています。");
            return 0;
        }
    }

例外が catchできました。

結果
個数がマイナスです。

演習

問題1

「IOException」の例外処理をする「try-catch」文で正しいのはどれですか?

  1. try { } catch { }
  2. try (IOException e) { } catch { }
  3. try { } catch (IOException e) { }
  4. try (IOException e) { } catch (IOException e) { }

問題2

「IOException」の例外処理をする「throws」文で正しいのはどれですか?

  1. public check() throws IOException { }
  2. public check(IOException e) throws { }
  3. public check() throws IOException(e) { }
  4. public check(throws IOException) { }

問題3

Nullを強制的に発生させる処理で、正しいのはどれですか?

  1. NullPointerException();
  2. throw NullPointerException();
  3. new NullPointerException();
  4. throw new NullPointerException();