- 1 1. はじめに:Javaで「try」が重要な理由
- 2 2. try構文の基本:書き方と動作の仕組み
- 3 3. catch・finally・throw・throws の使い分け
- 4 4. 応用パターン:try-with-resources と例外の伝播
- 5 例外の伝播:例外が上位のメソッドへ移動する仕組み
- 6 5. よくあるミス・アンチパターンと対策
- 7 6. 実践的なコード例:よく使う例外処理パターン
- 8 7. Javaのバージョン差・ライブラリ別の例外処理の特徴
- 9 1. Java バージョンごとの例外処理の進化
- 10 2. チェック例外と非チェック例外の扱い(再整理)
- 11 3. Spring(Spring Boot)における例外処理の特徴
- 12 4. 実務での例外処理設計の考え方
- 13 8. まとめ:try構文を正しく使えば、Javaコードは大きく安定する
- 14 ◆ 最後に
- 15 9. FAQ:Javaのtry・例外処理に関するよくある質問
- 15.1 Q1. try と catch は必ずセットで書く必要がありますか?
- 15.2 Q2. catch の例外型には何を指定すればいいですか?
- 15.3 Q3. finally ブロックは必ず書くべきですか?
- 15.4 Q4. try ブロックを小さくする理由は何ですか?
- 15.5 Q5. 「例外を握りつぶす」のはなぜ悪いのですか?
- 15.6 Q6. throw と throws の違いがよくわかりません。
- 15.7 Q7. try-with-resources は必ず使うべきですか?
- 15.8 Q8. Spring Boot では try/catch をほとんど書かないのはなぜですか?
- 15.9 Q9. 例外は多ければ多いほど良いのですか?
- 15.10 Q10. 例外処理のベストプラクティスを一言で言うと?
1. はじめに:Javaで「try」が重要な理由
Javaでプログラムを書いていると、必ず直面するのが「例外処理」です。ファイルの読み込み、ネットワーク通信、数値計算、ユーザー入力など、プログラムは常に予期せぬエラーに遭遇する可能性があります。こうした“例外”が発生したとき、何も対策をしていないと、プログラムは即座に停止し、処理が途中で終わってしまいます。
そこで登場するのが try を中心とした例外処理構文です。try は、エラーが起きる可能性のある処理を「安全に囲う」ための仕組みであり、Javaの安定した動作を支える極めて重要な文法です。
try構文が果たす役割
- プログラムが想定外のエラーで止まるのを防ぐ
- 異常時の処理(ログ出力・メッセージ表示・リソース解放など)を適切に制御できる
- 正常系と異常系の処理を明確に分離できる
- 実務で必須となる「安全性」「信頼性」を確保する
このように try は、Javaプログラムを安定させるための“安全装置”のような存在です。
初心者のうちは少しとっつきにくく感じる場合もありますが、一度理解すればコードの質が大きく向上します。
この記事が想定する読者
- Javaを学習し始めたばかりの人
- try / catch の正しい書き方があいまいな人
- try-with-resources や例外の伝播などを改めて整理したい人
- 実務レベルで例外処理のベストプラクティスを身につけたい人
本記事では、try の基本から発展的な書き方、よくあるミス、実践的なパターンまで、順番にわかりやすく解説していきます。
2. try構文の基本:書き方と動作の仕組み
例外処理を理解するうえで、まず押さえておきたいのが try / catch の基本構文です。Javaの例外処理は、「例外が発生する可能性のある処理」と「例外が発生した場合の処理」を明確に分けて書く仕組みになっています。
try / catch の基本構文
Javaで最も基本的な例外処理構文は以下の形になります。
try {
// 例外が発生する可能性のある処理
} catch (Exception e) {
// 例外が発生したときの処理
}try ブロック内の処理を実行している最中に例外が発生すると、処理は即座に中断され、catch ブロックに制御が移ります。逆に、例外が発生しなかった場合は catch は実行されず、そのまま次の処理へ進みます。
基本的な動作の流れ
- try ブロックの処理を順に実行
- 例外が発生した瞬間に中断される
- 該当する catch ブロックへジャンプ
- catch 内の処理を実行する
- 処理終了後、try/catch の外側の処理へ戻る
この流れによって、突然のエラーが起こっても、プログラム全体が止まることを防げます。
初心者にもわかりやすい例:ゼロ除算
最も理解しやすい例として「ゼロ除算」のコードを見てみましょう。
try {
int result = 10 / 0; // ゼロ除算 → 例外発生
System.out.println("結果:" + result);
} catch (ArithmeticException e) {
System.out.println("エラー:ゼロで割ることはできません。");
}ポイント
10 / 0によってArithmeticExceptionが発生- try内の残りの行(print文)は実行されない
- 代わりに catch 内のメッセージが出力される
このように、try は「問題が起きそうな部分」を囲うための構文であり、例外処理の入口として機能します。
catch の例外型はどう選ぶ?
catch の丸括弧内には、捕捉したい例外の“種類”を書く必要があります。
例:
catch (IOException e)
catch (NumberFormatException e)Javaには多くの例外クラスがあり、それぞれ「どんなエラーなのか」を表します。
初心者のうちは Exception で大まかに捕まえても問題ありませんが、実務ではできる限り具体的な例外型を指定した方が、原因分析やデバッグが容易になります。
例外が起きなかったらどうなる?
例外が発生しなかった場合、
- try ブロックを最後まで実行
- catch ブロックはスキップ
- そのまま次の処理へ進む
という流れになります。
例外は「あくまで異常時だけ」発生するものだと理解しておくと良いでしょう。
3. catch・finally・throw・throws の使い分け
Java の例外処理では、try と組み合わせて使用する構文が複数あります。
それぞれ役割が異なり、正しく使い分けることで、読みやすく安全性の高いコードを書くことができます。
ここでは catch / finally / throw / throws の4つについて、初心者にもわかりやすく解説します。
catch:発生した例外を受け取るブロック
catch は、try の中で発生した例外を処理するためのブロックです。
try {
int num = Integer.parseInt("abc"); // NumberFormatException
} catch (NumberFormatException e) {
System.out.println("数値に変換できません。");
}ポイント
tryの中で起きた例外だけを処理する- 例外型を指定することで、特定のエラーにだけ反応させることが可能
- 複数の
catchを並べて、例外ごとに処理を分けることもできる
try {
// 何らかの処理
} catch (IOException e) {
// ファイルのエラー
} catch (NumberFormatException e) {
// データ形式のエラー
}finally:例外が起きても必ず実行される処理
finally ブロックは、例外発生の有無にかかわらず必ず実行されるコードを記述する場所です。
try {
FileReader fr = new FileReader("data.txt");
} catch (IOException e) {
System.out.println("ファイルを開けませんでした。");
} finally {
System.out.println("処理を終了します。");
}よくある用途
- ファイルやネットワーク接続のクローズ
- データベース接続の切断
- 一時的に確保したリソースの解放
つまり、後始末を必ず実行したい時に使うのが finally です。
throw:例外を「自分で」発生させる
throw は、明示的に例外を発生させるためのキーワードです。
public void checkAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("年齢が不正です");
}
}使いどころ
- 不正な引数が渡された場合の警告
- 業務ロジック上、例外として扱いたいケース
- 「正常ではない状態」を判断したら強制的に例外にする
throw によって、開発者が意図的にプログラムの流れを例外へ切り替えることができます。
throws:例外を「呼び出し元へ渡す」宣言
メソッドの定義部分に記述し、
「このメソッドでは特定の例外が発生する可能性があるので、呼び出し側で処理してください」
という意味を持ちます。
public void readFile() throws IOException {
FileReader fr = new FileReader("test.txt");
}throws の役割
- メソッド内で発生する例外を自分で処理しない
- 「呼び出した側」に例外処理を任せる
- メソッドの宣言に明示し、例外の責任範囲を明確にする
実務では、
「どこで例外を握り、どこで上位に投げるか?」
という設計がコードの品質に大きく影響します。
4つの役割の違いを整理
| キーワード | 役割 |
|---|---|
| try | 例外が起きるかもしれない処理を囲う |
| catch | 発生した例外を捕まえて処理する |
| finally | 例外の有無にかかわらず必ず実行 |
| throw | 例外を開発者が手動で発生させる |
| throws | メソッドが例外を投げる可能性を宣言する |
この理解ができると、例外処理の全体像が明確になります。
4. 応用パターン:try-with-resources と例外の伝播
Java の例外処理は、基本の try / catch だけでも十分活用できますが、より安全で効率的に扱うための“応用パターン”も存在します。特に実務では、リソースの解放 や 例外の伝播 の扱い方がコード品質に大きく影響します。
ここでは、Java 7 以降で導入された try-with-resources と、例外がメソッドをまたいで伝わっていく 例外の伝播 の仕組みを解説します。
try-with-resources:リソースを自動でクローズする仕組み
ファイル、ソケット、データベース接続など、多くの処理には「リソース」の管理が伴います。
リソースは開いたら必ず閉じなければならず、従来は finally ブロックで手動で close() を書く必要がありました。
しかし、手動でのクローズは忘れやすく、例外が出ると適切に閉じられない危険もあります。
そこで登場したのが try-with-resources です。
try-with-resources の基本構文
try (FileReader fr = new FileReader("data.txt")) {
// ファイル操作
} catch (IOException e) {
System.out.println("ファイル読み込みに失敗しました。");
}try の丸括弧内で宣言したリソースは、
例外が発生しようがしまいが、自動的に close() が呼ばれます。
try-with-resources が便利な理由
- リソースの閉じ忘れがない
finallyに長いクローズ処理を書く必要がない- コードが短く、読みやすくなる
- close() 自体が例外を投げても安全に処理される
実務では、ファイル操作やDB接続が多いため、
可能な限り try-with-resources を採用するのが推奨されています。
複数リソースもまとめて扱える
try (
FileReader fr = new FileReader("data.txt");
BufferedReader br = new BufferedReader(fr)
) {
String line = br.readLine();
System.out.println(line);
}リソースを複数並べて書くこともでき、
すべて自動でクローズされるため非常に便利です。
例外の伝播:例外が上位のメソッドへ移動する仕組み
もうひとつ重要なのが「例外の伝播」です。
例外がメソッド内で発生した際、その場で try / catch で処理しない場合、
例外は 呼び出し元へそのまま伝わっていきます。
例外を伝播させる例(throws)
public void loadConfig() throws IOException {
FileReader fr = new FileReader("config.txt");
}このメソッドを呼び出した側は、例外処理を行う必要があります。
try {
loadConfig();
} catch (IOException e) {
System.out.println("設定ファイルが読み込めません。");
}伝播のメリット
- 下位メソッドに异常処理を詰め込みすぎず、責任を上位に委ねられる
- メソッド構造が明確になり、可読性が上がる
- 例外ログやハンドリングを一箇所に集中させられる
伝播のデメリット(注意点)
- どこで例外が握られるのか把握が必要
- 上位が例外処理を忘れるとプログラムが止まる
- 小さな処理にも throws を多用するとメソッド宣言が重くなる
実務では、
「どこで例外をキャッチし、どこで伝播させるか」
を設計段階で決めることが重要になります。
try-with-resources と例外伝播は組み合わせられる
public void readData() throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
System.out.println(br.readLine());
}
}- リソースは自動でクローズされる
- 例外は呼び出し元へ自然に伝わる
- コードが短くなり、安全性も高い
非常に実務向きの書き方です。
5. よくあるミス・アンチパターンと対策
Java の例外処理は非常に便利な仕組みですが、使い方を誤るとコードが読みにくくなったり、バグの温床になったりすることがあります。
特に初心者〜中級者が陥りやすい「アンチパターン(避けるべき書き方)」は多く、実務でも頻繁に問題となります。
ここでは、代表的なミスとその対策を分かりやすく解説します。
1. try ブロックが大きすぎる
try {
// 100行くらいある長い処理…
} catch (Exception e) {
// 例外発生時の処理
}問題点
- どの行が例外を出す可能性があるのかが不明瞭
- バグ発生時に原因特定が非常に難しい
- 不必要な部分まで例外処理の対象になる
対策
- 「例外発生の可能性がある部分だけ」を try で囲む
- ロジックと例外処理を明確に分離する
// 前処理
try {
loadConfig(); // 例外が出る可能性があるのはここだけ
} catch (IOException e) {
// エラー処理
}
// 後続処理2. catch を空のまま放置する(握りつぶす)
try {
int n = Integer.parseInt(input);
} catch (NumberFormatException e) {
// 何もしない(黙殺)
}これは最悪のアンチパターンです。
問題点
- 異常が起きたことが全くわからない
- バグが発見できず、デバッグ不可
- 実務では重大な障害に直結
対策
- 必ずログを書く、あるいはユーザーにエラーを伝える
- 最後の手段として再スローする方法もある
catch (NumberFormatException e) {
System.err.println("入力値が不正です:" + e.getMessage());
}3. 例外を広すぎる型で受け取る
catch (Exception e) {
// なんでも捕まえる
}問題点
- 何が起きたのか特定しづらい
- 本来捕まえるべきでない例外まで処理してしまう
- 必要なエラーが隠れてしまう可能性がある
対策
- 極力、具体的な例外型を指定する
- どうしても複数の例外をまとめたい場合は「マルチキャッチ」を使う
catch (IOException | NumberFormatException e) {
// 複数の例外をまとめて処理
}4. finally で例外を投げてしまう
finally {
throw new RuntimeException("finallyで例外");
}問題点
- try/catch で発生した“元の例外”が失われる
- スタックトレースが混乱し、デバッグが困難に
- 実務でこれをやると原因調査がほぼ不可能
対策
- finally には「後始末だけ」を記述する
- 例外を投げるロジックは入れない
5. リソースの close() を書き忘れる
従来の try/finally ではよく起きる問題です。
FileReader fr = new FileReader("data.txt");
// close() を書き忘れる → メモリリーク、ファイルロックが残る
対策:try-with-resources を使う
try (FileReader fr = new FileReader("data.txt")) {
// 安全に自動クローズ
}リソース管理が必要な処理では、基本的に try-with-resources が標準 と考えるべきです。

6. “例外は全部 throws で投げればいい”と思ってしまう
public void execute() throws Exception {
// 全部 throws に任せる
}問題点
- 呼び出し側が例外処理だらけになり、設計が破綻
- どこが責任を負うのか曖昧になる
対策
- 下位メソッドでは“握るべき例外”だけ捕まえる
- 重大な例外だけ上位へ伝播させる(バランスが大事)
アンチパターンを防ぐ基本原則
- try ブロックは必要最小限に
- catch は具体的な例外型を指定
- finally は「後始末」だけ
- 空の catch は絶対に避ける
- リソース処理は try-with-resources に統一
- 例外設計は「責任の所在」を意識
- ログは必ず残す
これらを守るだけでも、コードの品質は大幅に向上します。
6. 実践的なコード例:よく使う例外処理パターン
ここでは、Java の開発現場で実際によく使われる「例外処理パターン」を、具体的なコード例とともに紹介します。単なる文法理解にとどまらず、実務でそのまま応用できる内容を中心にまとめています。
1. ファイル読み込みでの例外処理(try-with-resources)
最も代表的な例が、ファイル操作の例外処理です。
ファイルアクセスは失敗しやすいため、例外処理が必須です。
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("ファイル読み込み中にエラーが発生しました: " + e.getMessage());
}ポイント
- try-with-resources によりクローズ処理が不要
- IOException を catch するのが基本
- 失敗時は原因をログに残すと調査しやすい
2. ユーザー入力の検証と例外処理
ユーザー入力はバグの温床です。
入力値を検証し、不適切な値は例外として扱うことがあります。
public int parseAge(String input) {
try {
int age = Integer.parseInt(input);
if (age < 0) {
throw new IllegalArgumentException("年齢は0以上である必要があります");
}
return age;
} catch (NumberFormatException e) {
throw new NumberFormatException("数値を入力してください");
}
}実務でよくある使い方
- 入力チェック
- エラーメッセージの制御
- “throw” を使って開発者側で意図的に例外化する
3. 複数 catch を使った例外区分
1つの処理で複数タイプの例外が起こり得る場合は、
catch を複数用意して「どんなエラーか」を判別します。
try {
processTask();
} catch (IOException e) {
System.out.println("I/Oエラー: " + e.getMessage());
} catch (NullPointerException e) {
System.out.println("想定外のnullが検出されました");
} catch (Exception e) {
System.out.println("その他のエラーが発生しました");
}メリット
- 何が起きたのか原因特定がしやすい
- 適切な処理に分岐させられる
4. 例外をログに記録して再スロー(実務で多い)
実務では「例外をログに残して、呼び出し元へさらに投げる」パターンがよくあります。
try {
processData();
} catch (IOException e) {
System.err.println("ログ: データ処理でIOエラーが発生しました");
throw e; // 呼び出し側に再度例外を伝播
}実務向きの理由
- ログを残すことで調査しやすい
- 例外ハンドリングを上位層に任せられる
5. API呼び出しやサービス通信時の例外処理
外部のAPIやサービスと通信するコードは失敗しやすいです。
try {
String response = httpClient.get("https://example.com/api");
System.out.println("レスポンス: " + response);
} catch (IOException e) {
System.out.println("通信エラーが発生しました。再試行してください。");
}注意点
- 通信は例外発生率が高い
- リトライ処理なども検討する
- ステータスコードによるエラーも別途扱う必要がある
6. 例外を独自クラスで定義する(高度なパターン)
プロジェクトが大きくなると、アプリ独自の例外を定義することがあります。
public class InvalidUserException extends Exception {
public InvalidUserException(String message) {
super(message);
}
}
public void validateUser(User user) throws InvalidUserException {
if (user == null) {
throw new InvalidUserException("ユーザー情報が不正です");
}
}メリット
- エラーの種類をプロジェクト固有にカスタマイズできる
- 業務ロジックに即した例外構造を設計できる
実務で知っておきたい例外処理のベストプラクティス
- try は最小単位で囲う
- 可能な限り try-with-resources を使う
- catch は具体的な例外型を指定
- 空の catch は絶対に書かない
- ログを残しつつ必要に応じて再スロー
- 例外の責任範囲を明確にする(どこで握り、どこで投げるか)
これらの考え方を取り入れると、実務でも安定した保守しやすいコードになります。
7. Javaのバージョン差・ライブラリ別の例外処理の特徴
Java の例外処理は古くからある仕組みですが、バージョンアップとともに便利な機能が追加され、書き方の幅も広がってきました。また、Spring などの実務で使われるフレームワークは独自の例外設計を持つことが多く、Java単体の例外処理とは異なる思想が必要になります。
ここでは、Java のバージョンごとの違いと、主要フレームワークでの例外処理の考え方を解説します。
1. Java バージョンごとの例外処理の進化
Java 7:try-with-resources の登場(革命的な変更)
Java 7 以前は、リソース解放を必ず finally に書く必要がありました。
FileReader fr = null;
try {
fr = new FileReader("data.txt");
} finally {
if (fr != null) fr.close();
}問題点
- コードが長い
- close() も例外を投げるためさらに try-catch が必要
- リソースリークが起きやすい
Java 7 の try-with-resources で解決
try (FileReader fr = new FileReader("data.txt")) {
// 読み込み
}- 自動で close() を実行
- finally 不要
- シンプルで安全
→ Java の実務で最も重要なアップデートのひとつ。
Java 8:ラムダ式と組み合わせたエラーハンドリング
Java 8 ではラムダ式が導入されたため、ストリーム処理の中でも例外が扱われるようになりました。
List<String> list = Files.lines(Paths.get("test.txt"))
.collect(Collectors.toList());ストリーム内で IOException が発生するとチェック例外の扱いが難しいため、
例外をラップして RuntimeException に変換するパターンが増えています。
Java 9 以降:try-with-resources の拡張
Java 9 ではすでに宣言済みの変数を try-with-resources に渡せるようになりました。
BufferedReader br = new BufferedReader(new FileReader("data.txt"));
try (br) {
System.out.println(br.readLine());
}メリット
- 事前にリソースを生成し、後から try-with-resources に載せられる
- コードの柔軟性向上
2. チェック例外と非チェック例外の扱い(再整理)
Java の例外は2種類に分類されます。
チェック例外(Checked Exceptions)
- IOException
- SQLException
- ClassNotFoundException
→ throws が必須。
非チェック例外(Unchecked Exceptions)
- NullPointerException
- IllegalArgumentException
- ArithmeticException
→ throws 不要。
→ ランタイムに発生。
実務では
業務的に回復可能なもの → チェック例外
プログラムの欠陥によるもの → 非チェック例外
という整理が一般的です。
3. Spring(Spring Boot)における例外処理の特徴
Java の代表的なフレームワークである Spring / Spring Boot では、例外処理の設計が少し異なります。
Spring の特徴
- 例外を「RuntimeException(非チェック例外)」に統一する設計が主流
- DAO層・Service層・Controller層で例外を分ける
@ExceptionHandlerや@ControllerAdviceで集中管理できる
例:Controller層の例外ハンドリング
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(Exception e) {
return ResponseEntity.status(500).body("サーバーエラーが発生しました");
}
}メリット
- 例外処理を1ヶ所にまとめられる
- 各Controllerに余計な try/catch を書かなくて済む
- 大規模サービスに向いている設計
4. 実務での例外処理設計の考え方
- 下位層では業務的に対応できる例外は握る
- 握れない例外は上位へ投げて Controller 層でまとめて対応する
- 外部API・DBアクセスなど失敗リスクの高い箇所は詳細なログを残す
- 例外の種類をプロジェクト内部で統一する(混在させない)
- 復帰可能な例外を見極める(リトライなど)
Java 単体の書き方よりも、
アプリケーション全体として挙動をどう設計するか が重要になります。
8. まとめ:try構文を正しく使えば、Javaコードは大きく安定する
本記事では、Javaの例外処理の中心となる try構文 を軸に、基本から実践、アンチパターンからバージョン差まで、幅広く解説してきました。例外処理は初心者にとって難しく感じられることが多いですが、正しく理解すればコードの品質を一段階引き上げる強力な武器になります。
ここで、重要なポイントを整理しておきましょう。
◆ try構文の役割を正しく理解する
- “例外が起きそうな処理” を安全に囲むための仕組み
- プログラムの異常停止を防ぎ、安定性を高める
- 正常系と異常系の流れを明確に分けられる
例外処理の第一歩は、try/catch の基本動作の理解から始まります。
◆ catch・finally・throw・throws の適切な使い分け
- catch:例外を捕まえてハンドリング
- finally:必ず実行したい後処理を書く
- throw:開発者が意図的に例外を発生させる
- throws:例外を呼び出し元に委ねる
これらの“役割の違い”を理解することで、例外処理の設計が一気に楽になります。
◆ try-with-resources は実務で必須
Java 7 で追加されたこの構文は、
「リソースを安全に自動で閉じる」
という大きなメリットを持ちます。
ファイル、ネットワーク、DBなど、リソースを扱うコードでは
try-with-resources を標準として使うのが現代のJava開発の常識です。
◆ よくあるミスを避ければ、一気に品質が向上する
- try を大きくしすぎる
- catch を空にする
- Exception を乱用して広く捕まえる
- finally に例外を投げる
- close() の書き忘れ
これらは初心者〜中級者が必ずつまずくポイントです。
1つ1つ意識して避けるだけで、見違えるほど良いコードになります。
◆ 実務では「どこで例外を処理するか」が重要
- 下位層で握るべき例外
- 上位へ投げるべき例外
- Spring などのフレームワークでは集中管理が一般的
例外処理は“コードの品質”だけでなく、
アーキテクチャ全体の設計 にも深く関わります。
◆ 最後に
try構文は、Javaの例外処理の基礎でありながら、
コードの安定性・可読性・保守性を大きく左右する重要な文法 です。
初心者のうちは難しく感じても、
- 例外の仕組み
- try-with-resources の使い方
- 必要最小限の try ブロック
- 適切な catch の設計
このあたりを意識するだけで、確実に理解が深まります。
そして実務では、
「どの層で例外を処理するか」 を決めたうえで、統一した例外設計を行うことで、より強固なアプリケーションを構築できます。
本記事が、Javaのtry構文と例外処理を理解し、安定したコードを書くための手助けになれば幸いです。
9. FAQ:Javaのtry・例外処理に関するよくある質問
Q1. try と catch は必ずセットで書く必要がありますか?
A. はい、基本的にはセットです。ただし例外として try-with-resources+finally の組み合わせも可能です。
通常の構文では、try { ... } catch (...) { ... }
は必ずセットで使います。
ただし、以下の組み合わせも有効です。
try + finallytry-with-resources + catchtry-with-resources + finally
Q2. catch の例外型には何を指定すればいいですか?
A. 原則として「その処理で発生し得る具体的な例外型」です。
例えば:
- ファイル →
IOException - 数値変換 →
NumberFormatException - 配列アクセス →
ArrayIndexOutOfBoundsException
「とりあえず Exception で全部捕まえる」は便利な反面、
“何が起きたのかわからなくなる”ため、実務では避けるべきです。
Q3. finally ブロックは必ず書くべきですか?
A. 必須ではありません。リソースの解放など「必ず実行したい処理」がある場合にのみ使います。
Java 7 以降は
- ファイル
- ソケット
- DB接続
などのリソースは try-with-resources に任せるのが一般的で、finally を書かないケースも増えています。
Q4. try ブロックを小さくする理由は何ですか?
A. 例外が発生しうる箇所が明確になり、原因の特定が圧倒的にしやすくなるからです。
逆に try を大きくすると、
- どこでエラーが起きたかわからない
- 正常なコードまで例外処理の対象になる
- デバッグが困難
などのデメリットが生じます。
Q5. 「例外を握りつぶす」のはなぜ悪いのですか?
A. エラーが隠れてしまい、不具合の原因が永遠にわからなくなるからです。
例:
catch (Exception e) {
// 何もしない ← NG
}これは実務では最も嫌われる書き方です。
少なくともログ出力やメッセージ表示を行いましょう。
Q6. throw と throws の違いがよくわかりません。
A. throw は「例外を発生させる」、throws は「例外を上位に投げる宣言」です。
throw:実際に例外を投げる(行動)throws:例外が発生する可能性を宣言する(事前告知)
例:
throw new IllegalArgumentException(); // 今ここで投げる
public void load() throws IOException {} // 発生し得ると宣言Q7. try-with-resources は必ず使うべきですか?
A. リソース管理が必要な処理では「ほぼ必須」です。
- 自動で close()
- finally が不要
- コードが簡潔
- 例外が出ても安全
現代の Java 開発では、try-with-resources は標準と考えて問題ありません。
Q8. Spring Boot では try/catch をほとんど書かないのはなぜですか?
A. Spring は例外を一元管理する仕組み(@ExceptionHandler / @ControllerAdvice)があるためです。
個々のメソッドに try/catch を書く必要がなく、
- Controller層でまるごと例外をキャッチ
- エラーレスポンスを統一
- 業務層はロジックに集中
といった構造を作りやすくなっています。
Q9. 例外は多ければ多いほど良いのですか?
A. いいえ。必要以上に例外を投げるとコードが読みにくくなります。
意識すべきは
- 「業務的に回復可能な例外」だけ握る
- 「プログラムが壊れている例外」は RuntimeException で扱う
- 責任範囲(どこで例外を処理するか)を明確にする
という設計思想です。
Q10. 例外処理のベストプラクティスを一言で言うと?
A. “tryは小さく、catchは具体的に、finallyは必要なときだけ、リソースは自動で閉じる” です。
これを守るだけで、例外処理の質は大きく向上します。
