プログラミングをしていると、必ずといっていいほど予期しないエラーや問題が発生します。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 非チェック例外
非チェック例外は、コンパイル時にはチェックされない例外であり、主にプログラム内の論理エラーに関連しています。これらは実行時に発生する例外で、例えばNullPointerException
やArrayIndexOutOfBoundsException
などがあります。
主な非チェック例外:
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
構文による例外のキャッチ方法や、throws
とthrow
キーワードの使い方、finally
ブロックの使用方法、そしてチェック例外と非チェック例外の違いについて解説しました。また、カスタム例外を定義し、独自のエラーハンドリングを行う方法も紹介しました。
例外処理は、堅牢で安定したプログラムを作る上で非常に重要な要素です。次章では、Javaの標準ライブラリを学び、日常的に使える便利なクラスやメソッドを探っていきます。