Javaプログラミング言語では、エラーやその他の例外的な出来事を扱うために例外を使用します。
例外とは、プログラムの実行中に発生し、命令の通常の流れを妨げる出来事です。 Javaでは例外は明示的に発生させ、この例外を発生させる行為を_例外をスローする_と呼びます。 例外を処理する行為を_例外をキャッチする_と呼びます。
Javaでは、すべての例外はExceptionクラスのサブクラスであり、Exceptionクラス自体はThrowableのサブクラスです。
Javaでは、例外を2つの種類に分けています。
_チェック例外_とは、アプリケーションが事前に予測し、そこから回復すべき例外的な状況です。
チェック例外の例として、存在しないファイルをメソッドが読み込もうとしたときに発生するFileNotFoundExceptionがあります。
この種類の例外はコンパイル時にチェックされます。チェック例外をスローするメソッドは、そのことをメソッドシグネチャに指定する必要があり、チェック例外をスローする可能性のあるメソッドを呼び出すコードは、それを処理しなければコンパイルされません。
すべてのチェック例外は、RuntimeExceptionを継承していないExceptionのサブクラスです。
_非チェック例外_とは、アプリケーションが通常は予測も回復もできない例外的な状況です。
非チェック例外の例として、非nullの値を受け取ることを想定しているメソッドがnullを受け取ったときに発生するNullPointerExceptionがあります。
この種類の例外はコンパイル時にチェックされません。非チェック例外をスローするメソッドは、そのことをメソッドシグネチャに指定する必要はなく、非チェック例外をスローする可能性のあるメソッドを呼び出すコードも、それを処理する必要はありません。
すべての非チェック例外はRuntimeExceptionを継承しており、RuntimeException自体はExceptionを拡張したものです。
Javaのメソッドは、throw文を使うことで例外をスローできます。
メソッドからチェック例外をスローするときは、以下の例のように、throwsキーワードを使ってそのことをメソッドシグネチャに指定する必要があります。
これにより、呼び出す側のコードは、例外がスローされる可能性を予測し、それに応じて処理することが求められます。
public class InsufficientBalanceException extends Exception {
}
public class BankAccount {
public void withdraw(double amount) throws InsufficientBalanceException {
if (balance < amount) {
throw new InsufficientBalanceException();
}
// rest of the method implementation
}
}
メソッドから非チェック例外をスローするときは、メソッドシグネチャにそのことを指定する必要はありません。ただし、指定することもできます。
public class BankAccount {
public void withdraw(double amount) {
if (amount < 0) {
throw new IllegalArgumentException("Cannot withdraw a negative amount");
}
// rest of the method implementation
}
}
Javaで例外を処理するには、try、catch、finallyというキーワードを使います。
tryブロックで囲みます。tryブロックの後には、tryブロックでスローされた例外をキャッチする1つ以上のcatchブロックが続きます。catchブロックの後には、例外がスローされたかどうかに関係なく、tryブロックの後に必ず実行されるfinallyブロックを置くこともできます。次の例は、これらのキーワードがどのように機能するかを示しています。
public class ATM {
public void withdraw(BankAccount bankAccount, double amount) {
try {
System.out.println("Withdrawing " + amount);
bankAccount.withdraw(amount);
System.out.println("Withdrawal succeeded");
} catch (InsufficientBalanceException) {
System.out.println("Withdrawal failed: insufficient balance");
} catch (RuntimeException e) {
System.out.println("Withdrawal failed: " + e.getMessage());
} finally {
System.out.println("Current balance: " + bankAccount.getBalance());
}
}
}
この例では、例外がスローされなかった場合、次のように出力されます。
Withdrawing 10.0
Withdrawal succeeded
Current balance: 5.0
しかし、bankAccount.withdraw(amount)の文がInsufficientBalanceExceptionをスローした場合は、次のように出力されます。
Withdrawing 10.0
Withdrawal failed: insufficient balance
Current balance: 5.0
また、bankAccount.withdraw(amount)によって非チェック例外がスローされた場合は、次のように出力されます。
Withdrawing -10.0
Withdrawal failed: Cannot withdraw a negative amount
Current balance: 5.0
Javaには、_エラー_と呼ばれる、アプリケーションの外部にある深刻な問題を表す別のカテゴリもあります。
エラーの例として、システムで利用できる以上のメモリをアプリケーションが使おうとしたときに発生するOutOfMemoryErrorがあります。
非チェック例外と同様に、エラーはコンパイル時にチェックされません。 違いは、エラーがシステムレベルの問題を表しており、通常はアプリケーションではなくJava仮想マシンや環境によってスローされる点です。 アプリケーションは、通常、エラーをキャッチしたり処理しようとしたりすべきではありません。
JavaのすべてのエラーはErrorクラスを継承しています。
前述のとおり、例外は命令の通常の流れを妨げる出来事であり、_例外的な出来事_を処理するために使われます。 したがって、アプリケーションでフロー制御に例外を使うことはおすすめしません。