【Java入門シリーズ】第8章例外処理 〜エラーに対処する方法〜

【Java入門シリーズ】第8章: 例外処理 〜エラーに対処する方法〜

プログラミングをしていると、必ずといっていいほど予期しないエラーや問題が発生します。Javaでは、これらのエラーを適切に処理し、プログラムが予期せぬクラッシュを引き起こさないようにする仕組みが用意されています。それが例外処理(Exception Handling)です。

この章では、Javaの例外処理について詳しく解説します。具体的には、例外の基本概念、try-catch構文、例外の種類、throwsおよびthrowキーワード、finallyブロックの使用方法について学びます。


8.1 例外とは?

例外(Exception)とは、プログラムの実行中に発生する予期しない問題を指します。例えば、存在しないファイルを開こうとしたり、0で割り算をしようとしたりすると、プログラムが正常に動作できなくなります。このような問題が発生すると、Javaは例外をスローし、それに対処しない限りプログラムはクラッシュします。

例外処理を正しく行うことで、プログラムが予期しない問題に対して適切に対応し、クラッシュを防ぐことができます。

8.1.1 例外の発生例

次のコードでは、0で割ろうとするとArithmeticExceptionが発生します。

public class Main {
public static void main(String[] args) {
int result = 10 / 0; // ここで例外が発生
System.out.println(result);
}
}

実行結果は次の通りです。

Exception in thread "main" java.lang.ArithmeticException: / by zero

この例では、0での除算が原因で例外が発生し、プログラムは途中でクラッシュします。このような問題に対処するために、例外処理を行います。


8.2 try-catch構文

Javaでは、例外が発生する可能性がある部分をtryブロックに書き、例外が発生した場合の処理をcatchブロックに記述します。この構文を使って例外をキャッチし、エラーに対して適切な対応を取ることができます。

8.2.1 try-catchの基本構文

try-catch構文の基本的な形は以下の通りです。

try {
// 例外が発生する可能性のあるコード
} catch (例外クラス 変数名) {
// 例外が発生した場合の処理
}

次の例では、0での除算をtry-catchでキャッチし、エラーが発生したときにメッセージを表示します。

public class Main {
public static void main(String[] args) {
try {
int result = 10 / 0; // 例外が発生する可能性がある部分
} catch (ArithmeticException e) {
System.out.println("エラー: 0で割ることはできません。");
}
}
}

実行結果:

エラー: 0で割ることはできません。

この例では、ArithmeticExceptionがスローされた際に、catchブロックが実行され、プログラムのクラッシュを防ぎました。

8.2.2 複数の例外をキャッチする

複数の種類の例外が発生する可能性がある場合、catchブロックを複数定義することができます。

public class Main {
public static void main(String[] args) {
try {
int[] numbers = {1, 2, 3};
System.out.println(numbers[10]); // 配列の範囲外アクセス
int result = 10 / 0; // 0での割り算
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("エラー: 配列のインデックスが範囲外です。");
} catch (ArithmeticException e) {
System.out.println("エラー: 0で割ることはできません。");
}
}
}

実行結果:

エラー: 配列のインデックスが範囲外です。

このように、異なる種類の例外に対して異なる対応をすることができます。

8.2.3 すべての例外をキャッチする

すべての例外をキャッチするには、Exceptionクラスをcatchブロックで指定します。Exceptionクラスは、すべての例外の親クラスです。

public class Main {
public static void main(String[] args) {
try {
int result = 10 / 0;
} catch (Exception e) {
System.out.println("エラーが発生しました: " + e.getMessage());
}
}
}

8.3 throwsとthrowキーワード

Javaでは、メソッド内で例外が発生する可能性があることを宣言する方法として、throwsキーワードが使われます。また、プログラム内で手動で例外を発生させるには、throwキーワードを使用します。

8.3.1 throwsキーワード

throwsキーワードを使うことで、そのメソッドが例外をスローする可能性があることを明示できます。メソッドの呼び出し元で例外をキャッチしなければならないことを示すために使われます。

次の例では、divideメソッドが例外をスローする可能性があることをthrowsで宣言しています。

public class Main {
public static void main(String[] args) {
try {
int result = divide(10, 0);
System.out.println(result);
} catch (ArithmeticException e) {
System.out.println("エラー: 0で割ることはできません。");
}
}

public static int divide(int a, int b) throws ArithmeticException {
return a / b;
}
}

8.3.2 throwキーワード

throwキーワードを使うことで、特定の条件下で例外を手動で発生させることができます。

public class Main {
public static void main(String[] args) {
try {
checkAge(15);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}

public static void checkAge(int age) {
if (age < 18) {
throw new IllegalArgumentException("年齢が18歳未満です。");
}
System.out.println("年齢は適切です。");
}
}

実行結果:

年齢が18歳未満です。

8.4 finallyブロック

finallyブロックは、例外の有無にかかわらず必ず実行されるコードを記述するために使います。try-catchの後にfinallyを追加することで、ファイルのクローズやデータベース接続の解放など、必ず実行したい処理を記述できます。

8.4.1 finallyの基本構造

try-catch-finallyの基本構造は以下の通りです。

try {
// 例外が発生する可能性があるコード
} catch (例外クラス 変数名) {
// 例外が発生した場合の処理
} finally {
// 常に実行される処理
}

例えば、次のコードでは、例外が発生してもファイルを閉じる処理がfinallyブロックで必ず実行されます。

public class Main {
public static void main(String[] args) {
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("エラー: 0で割ることはできません。");
} finally {
System.out.println("プログラム終了時に必ず実行される処理");
}
}
}

実行結果:

エラー: 0で割ることはできません。
プログラム終了時に必ず実行される処理

8.4.2 finallyの使用例

次の例では、ファイル操作において、例外が発生した場合でも必ずファイルをクローズする処理を実行しています。

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner scanner = null;
try {
File file = new File("sample.txt");
scanner = new Scanner(file);
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
System.out.println("ファイルが見つかりません。");
} finally {
if (scanner != null) {
scanner.close();
System.out.println("ファイルを閉じました。");
}
}
}
}

このプログラムでは、ファイルが見つからない場合でも、finallyブロックで必ずファイルを閉じる処理が実行されます。


8.5 例外の種類

Javaには、多くの種類の例外が用意されており、これらは大きくチェック例外(Checked Exceptions)と非チェック例外(Unchecked Exceptions)に分類されます。

8.5.1 チェック例外

チェック例外は、コンパイル時にチェックされる例外です。これらの例外は、プログラムの正しい実行に必須のリソースや外部環境の操作に関連しています。たとえば、ファイル操作やネットワーク通信などがチェック例外に該当します。チェック例外が発生する可能性があるメソッドは、throwsキーワードでその例外を宣言する必要があります。

主なチェック例外:

  • IOException
  • SQLException
  • FileNotFoundException

8.5.2 非チェック例外

非チェック例外は、コンパイル時にはチェックされない例外であり、主にプログラム内の論理エラーに関連しています。これらは実行時に発生する例外で、例えばNullPointerExceptionArrayIndexOutOfBoundsExceptionなどがあります。

主な非チェック例外:

  • NullPointerException
  • ArithmeticException
  • ArrayIndexOutOfBoundsException

8.6 カスタム例外の作成

Javaでは、標準の例外クラスだけでなく、独自の例外を定義することも可能です。これをカスタム例外と呼びます。カスタム例外を作成するには、Exceptionクラスを継承します。

8.6.1 カスタム例外の定義

次の例では、InvalidAgeExceptionというカスタム例外を定義しています。

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

8.6.2 カスタム例外の使用

カスタム例外を使用して、特定の条件で例外をスローすることができます。

public class Main {
public static void main(String[] args) {
try {
checkAge(15);
} catch (InvalidAgeException e) {
System.out.println(e.getMessage());
}
}

public static void checkAge(int age) throws InvalidAgeException {
if (age < 18) {
throw new InvalidAgeException("年齢が18歳未満です。");
}
System.out.println("年齢は適切です。");
}
}

実行結果:

年齢が18歳未満です。

8.7 Javaの例外処理まとめ

この章では、Javaの例外処理について学びました。try-catch構文による例外のキャッチ方法や、throwsthrowキーワードの使い方、finallyブロックの使用方法、そしてチェック例外と非チェック例外の違いについて解説しました。また、カスタム例外を定義し、独自のエラーハンドリングを行う方法も紹介しました。

例外処理は、堅牢で安定したプログラムを作る上で非常に重要な要素です。次章では、Javaの標準ライブラリを学び、日常的に使える便利なクラスやメソッドを探っていきます。