これまで「クラスとインスタンス」「継承・インターフェース・ポリモーフィズム」とJavaのオブジェクト指向の基本を紹介してきました。今回はその流れとは少し離れて、Javaにおける例外処理の基本を紹介します。try-catch-finallyのような基本構文に加えて、Java特有の「チェック例外」という仕組みも扱います。
対象読者
- クラス・継承・インターフェースといったJavaの基本的なオブジェクト指向の概念を理解している方
- Javaの例外処理の基本(
try-catch-finally、チェック例外と非チェック例外の違い)を整理しておきたい方 - 他言語の経験はあるが、Javaの例外処理特有のルールを押さえておきたい方
独自の例外設計パターンやマルチスレッド環境での例外伝播など、より発展的な内容は扱いません。あくまで基本構文と、チェック例外という考え方を紹介する内容です。
検証環境
Java 25(LTS、2025年9月リリース)を前提にしています。2026年3月にリリースされたJava 26は非LTSでサポート期間が6か月に限定されている1ため、学習用途で環境を作るなら、現行LTSであるJava 25を使うのが無難です。
例外とは何か
例外(Exception)とは、プログラムの実行中に発生し、通常の処理の流れを中断させるイベントのことです。Oracleの公式チュートリアルでは、例外が発生した際のJVMの処理は次のように説明されています2。
- メソッド内でエラーが発生すると、JVMは例外オブジェクトを生成し、それをスロー(throw)する
- JVMはメソッドの呼び出し履歴(コールスタック)を逆順にたどり、その例外を処理できるハンドラ(
catchブロック)を探す - 適切なハンドラが見つかれば、そこに処理が移る
- どのメソッドでも適切なハンドラが見つからなければ、プログラムは異常終了する
つまり例外処理とは、「どこかで発生したエラーを、呼び出し元へと順にさかのぼりながら伝え、適切な場所で処理する仕組み」だと言えます。
try-catch-finallyの基本
例外を処理する基本構文がtry-catch-finallyです3。
try {
// 例外が発生する可能性のある処理
} catch (ExceptionType e) {
// 例外が発生した場合の処理
} finally {
// 例外の有無にかかわらず必ず実行される処理
}
-
tryブロック:例外が発生する可能性のある処理を書く -
catchブロック:tryブロック内で対象の例外が発生した場合に実行される。複数のcatchを並べて、例外の型ごとに異なる処理を書くこともできる -
finallyブロック:例外が発生してもしなくても必ず実行される。ファイルやコネクションのクローズなど、後片付けの処理に使われる
Java SE 7以降では、1つのcatchブロックで複数の例外型をまとめて扱うこともできます3。
try {
// 何らかの処理
} catch (IOException | SQLException e) {
// IOExceptionとSQLExceptionをまとめて処理
System.out.println("エラーが発生しました: " + e.getMessage());
}
チェック例外と非チェック例外
Javaの例外処理を理解するうえで、他の言語にあまり見られない特徴が「チェック例外(Checked Exception)」の存在です。Javaの例外は大きく2種類に分かれます4。
| 観点 | チェック例外 | 非チェック例外(unchecked) |
|---|---|---|
| 該当クラス |
Exceptionとそのサブクラス(RuntimeException系を除く) |
RuntimeException・Errorとそのサブクラス |
| コンパイラの要求 |
catchするかthrowsで宣言するかが必須
|
特に要求されない |
| 主な原因 | ファイルが存在しない、ネットワークが切断されたなど、回復可能な問題 | nullを参照した、配列の範囲外にアクセスしたなど、プログラム側の誤り |
| 代表例 | IOException |
NullPointerException、ArithmeticException、IndexOutOfBoundsException
|
公式チュートリアルでは、どちらにすべきかの判断基準として、次の指針が示されています4。
クライアント(呼び出し元)が合理的にその例外から回復できると期待できるなら、チェック例外にすべきである。クライアントが回復のために何もできないのであれば、非チェック例外にすべきである。
Pythonなど多くの言語にはこの「チェック例外」に相当する仕組みが存在しないため、他言語の経験がある場合はJava特有のルールとして意識しておく必要があります。
throws宣言:呼び出し元に処理を委ねる
チェック例外が発生しうるメソッドは、その場でcatchするか、throwsを使って「このメソッドはこの例外を投げる可能性がある」と宣言し、呼び出し元に処理を委ねるかのどちらかを選ぶ必要があります。これはcatch or specify要件と呼ばれています5。
public void writeList() throws IOException {
// IOExceptionが発生しうる処理
}
公式チュートリアルでは、ライブラリとして提供するクラスの場合、その場で例外をすべてキャッチしてしまうより、呼び出し元(さらにその先の呼び出し元)に処理を委ねたほうが適切な場合がある、と説明されています5。なお、throwsが必須なのはチェック例外のみで、非チェック例外については宣言してもしなくても構いません。
try-with-resources:リソースを確実に閉じる
ファイルやデータベース接続のように、使い終わったら明示的に閉じる必要があるリソースを扱う際には、try-with-resourcesという構文が使えます6。Java SE 7で導入されたもので、AutoCloseableインターフェースを実装したリソースを、tryブロックを抜ける際に自動的にクローズしてくれます。
try (FileReader fr = new FileReader(path);
BufferedReader br = new BufferedReader(fr)) {
return br.readLine();
}
これを使わずに従来のtry-finallyで書くと、次のようにfinallyブロックで手動クローズする必要があり、close()自体が例外を投げた場合の扱いも煩雑になります。
FileReader fr = new FileReader(path);
BufferedReader br = new BufferedReader(fr);
try {
return br.readLine();
} finally {
br.close();
fr.close();
}
try-with-resourcesを使うことで、リソースが確実に閉じられるようになり、コードもシンプルになります6。
独自の例外クラスを作る
Javaに標準で用意されている例外だけでなく、独自の例外クラスを定義することもできます。公式チュートリアルでは、次のような場合に独自の例外クラスの作成を検討するとよいとされています7。
- Javaの標準ライブラリにない、アプリケーション固有のエラーを表現したいとき
- 複数の関連する例外を、階層立てて扱いたいとき
多くの場合、独自の例外はExceptionを継承して作成します(チェック例外になります)。Errorは、JVMが処理を継続できないような深刻な問題のためのクラスなので、通常のアプリケーション開発では継承しません7。また、例外クラスの名前にはExceptionという接尾辞を付けるのが慣例です。
public class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
}
public class Animal {
String name;
int age;
public Animal(String name, int age) throws InvalidAgeException {
if (age < 0) {
throw new InvalidAgeException(name + "の年齢に負の数は指定できません");
}
this.name = name;
this.age = age;
}
}
try {
Animal animal = new Animal("ポチ", -1);
} catch (InvalidAgeException e) {
System.out.println("エラー: " + e.getMessage());
}
// エラー: ポチの年齢に負の数は指定できません
InvalidAgeExceptionはExceptionを継承しているためチェック例外となり、Animalのコンストラクタを呼び出す側は、catchするかthrowsで宣言するかをコンパイラによって強制されます。
まとめ
- 例外は、発生した箇所からコールスタックをさかのぼりながら、適切な
catchブロックへと伝えられる -
try-catch-finallyが例外処理の基本構文で、finallyは例外の有無にかかわらず必ず実行される - Javaの例外は「チェック例外」と「非チェック例外」に分かれ、チェック例外は
catchまたはthrowsでの対応がコンパイラによって強制される -
throws宣言は、チェック例外の処理を呼び出し元に委ねるための仕組み -
try-with-resourcesを使うと、AutoCloseableなリソースを確実にクローズできる - 独自の例外クラスは
Exceptionを継承して作成するのが基本で、クラス名にはExceptionの接尾辞を付けるのが慣例
チェック例外の存在は、他言語の経験があるとJava特有の仕組みとして戸惑いやすいポイントですが、「呼び出し元が回復できる可能性のあるエラーかどうか」という判断基準を押さえておくと、標準ライブラリの例外設計も読み解きやすくなります。
脚注
参考資料
- What Is an Exception? | The Java™ Tutorials (Oracle)
- Catching and Handling Exceptions | The Java™ Tutorials (Oracle)
- Unchecked Exceptions — The Controversy | The Java™ Tutorials (Oracle)
- Specifying the Exceptions Thrown by a Method | The Java™ Tutorials (Oracle)
- The try-with-resources Statement | The Java™ Tutorials (Oracle)
- Creating Exception Classes | The Java™ Tutorials (Oracle)
- Java 26 正式リリース — ラーゲイト株式会社
-
Java 26は2026年3月18日にリリースされた非LTS版で、サポート期間は6か月間(2026年9月まで)。現行LTSはJava 25(2025年9月リリース)。詳細はJava 26 正式リリース — ラーゲイト株式会社を参照。 ↩
-
Catching and Handling Exceptions / The catch Blocks | The Java™ Tutorials (Oracle) ↩ ↩2
-
Unchecked Exceptions — The Controversy | The Java™ Tutorials (Oracle) ↩ ↩2
-
Specifying the Exceptions Thrown by a Method | The Java™ Tutorials (Oracle) ↩ ↩2
-
The try-with-resources Statement | The Java™ Tutorials (Oracle) ↩ ↩2
-
Creating Exception Classes | The Java™ Tutorials (Oracle) ↩ ↩2