7日目のゴール
7日目のテーマは
「例外処理に“自分の方針”を持つ」 ことです。
文法としての try / catch を知っている
なんとなく例外を握りつぶさないことも分かっている
ここから一歩進んで、
どこで try / catch を書くか
どこで throws で上に任せるか
どこで独自例外に変換するか
どこを“最後の砦”にするか
を、自分の言葉で説明できる状態を目指します。
これまで作ってきた「例外処理アプリ」の全体像
レイヤー構造で振り返る
6日間で、あなたはこんな構造を自然に扱えるようになっています。
下の層(Java 標準ライブラリ)
ファイル I/O、数値変換、割り算など
IOException, NumberFormatException, ArithmeticException などの“技術的な例外”が飛ぶ場所
中間の層(機能クラス・ラッパークラス)
FileLoader、DivideFeature など
技術的な例外を受け取り、必要なら独自例外に変換する場所
上の層(アプリ本体・UI)
FileApp、ExceptionApp の main など
ユーザー入力を受け取り、機能を呼び出し、
例外をユーザー向けメッセージに変換する場所
さらに、一番外側には
「予期しない例外をまとめて受け止める最後の砦」がありました。
この構造を、今日はもう一度“設計目線”で整理します。
例外処理の「4つのレベル」を言葉にしてみる
レベル1:技術的な例外をそのまま受け止める
最初に触れたのは、NumberFormatException や ArithmeticException を
try / catch で直接受け止める形でした。
try {
int a = Integer.parseInt(aStr);
int b = Integer.parseInt(bStr);
int result = a / b;
System.out.println("結果: " + result);
} catch (NumberFormatException e) {
System.out.println("整数として解釈できない入力があります。数字だけを入力してください。");
} catch (ArithmeticException e) {
System.out.println("0 で割ることはできません。2つ目の数には 0 以外を入力してください。");
}
Javaここで学んだのは、
例外には“種類”があり、種類ごとにメッセージを変えられるcatch (Exception) で全部まとめると意味が消える
ということでした。
レベル2:throws で「ここでは決めない」と宣言する
ファイル読み込みでは、throws IOException を使って
「このメソッドでは例外を処理しない」と宣言しました。
public static void printFile(String fileName) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
}
Javaここで学んだのは、
例外を“上の層に任せる”という設計ができる
どの層が例外処理の責任を持つかを決めるのも設計
ということでした。
レベル3:独自例外で「アプリの言葉」に変換する
5日目では、
IOException をそのまま扱うのではなく、FileLoadException という独自例外に包んで投げ直しました。
public class FileLoadException extends Exception {
public FileLoadException(String message, Throwable cause) {
super(message, cause);
}
}
Javapublic static void printFile(String fileName) throws FileLoadException {
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
throw new FileLoadException("ファイルの読み込みに失敗しました: " + fileName, e);
}
}
Javaここで学んだのは、
技術的な例外(IOException)
アプリの意味を持った例外(FileLoadException)
を分けることで、
上の層は「このアプリにとっての失敗」だけを意識できる
詳細は cause(元の例外)から辿れる
という構造を作れる、ということでした。
レベル4:アプリ全体の「最後の砦」を置く
6日目では、
アプリ全体を try { runApp(); } catch (Exception e) で囲みました。
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
runApp(scanner);
} catch (Exception e) {
System.out.println("予期しないエラーが発生しました。アプリを終了します。");
System.out.println("(開発者向け情報)" + e.getClass().getName() + ": " + e.getMessage());
} finally {
scanner.close();
}
}
Javaここで学んだのは、
機能内で想定済みの例外は、その機能内で処理する
それでも漏れてきた“想定外の例外”は、一番外側でまとめて受け止める
という二重の安全ネットの考え方でした。
7日目の例題:自分の「例外処理ポリシー」をコードにする
小さな「計算+ファイル」アプリで方針をまとめる
最後に、これまでの考え方を
ぎゅっと詰め込んだサンプルをひとつ書いてみます。
やることはシンプルです。
メニューで「1: 割り算」「2: ファイル読み込み」「0: 終了」
割り算機能は NumberFormatException / ArithmeticException を機能内で処理
ファイル機能は FileLoadException を機能内で処理
それ以外の予期しない例外は main の外側で受け止める
コードはほぼ 6日目と同じですが、
今日は「方針」という目で眺めてみてください。
public class ExceptionApp {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
runApp(scanner);
} catch (Exception e) {
System.out.println("予期しないエラーが発生しました。アプリを終了します。");
System.out.println("(開発者向け情報)" + e.getClass().getName() + ": " + e.getMessage());
} finally {
scanner.close();
}
}
private static void runApp(Scanner scanner) {
while (true) {
System.out.println();
System.out.println("=== 例外処理アプリ メニュー ===");
System.out.println("1: 割り算機能");
System.out.println("2: ファイル読み込み機能");
System.out.println("0: 終了");
System.out.print("番号を選んでください: ");
String choice = scanner.nextLine();
if ("0".equals(choice)) {
System.out.println("終了します。");
break;
} else if ("1".equals(choice)) {
DivideFeature.run(scanner);
} else if ("2".equals(choice)) {
FileFeature.run(scanner);
} else {
System.out.println("不正な入力です。0, 1, 2 のいずれかを入力してください。");
}
}
}
}
Javaここに、あなたのポリシーが全部乗っています。
「ユーザーの入力ミス」は機能内で丁寧に扱う
「技術的な失敗」は独自例外に変換して上に伝える
「想定外のバグ」はアプリ全体の外側で受け止めて、きれいに終わる
これが、7日間かけて育ててきた“例外処理の設計感覚”です。
これから先のための「自分へのチェックリスト」
コードを書くとき、こんな問いを自分に投げてみてほしい
この処理は、どこで失敗しうるか?
それは事前チェックで防げるか?
それとも例外に任せるべきか?
この例外は、どの層で扱うのが自然か?
ここで catch すべきか?
それとも throws で上に任せるべきか?
この失敗は、ユーザーにどう伝えるのが親切か?
技術的な詳細は、どこに残しておくべきか?
この問いに、
自分なりの答えを持てるようになったとき、
あなたの例外処理は「場当たり的な対処」から
「意図のある設計」 に変わります。
7日目のまとめ:あなたはもう「例外が怖くない側の人」
最後に、7日間の本質を一言でまとめるとこうです。
例外は「バグの証拠」ではなく
「異常を知らせるための、ちゃんと設計された仕組み」 である。
try / catch は「赤いエラーを消すための消しゴム」ではなく
「アプリの振る舞いとユーザー体験をデザインするための道具」 である。
ここまで付き合ってくれたあなたは、
もう「例外が出ると怖い」側ではなく、
「例外を使ってアプリを強くできる」側 に立っています。
あとは、実際の小さなアプリをひとつ決めて、
今日までの方針を意識しながら、
自分の手で例外処理を組み込んでみてください。
そのコードは、もう立派に“プロの設計の入口”にいます。
