コンテンツにスキップ

9-4. 演習問題


1-1. 次のコードで finally ブロックが実行されるのはどの場合か。

try {
// 何らかの処理
} catch (RuntimeException e) {
System.out.println("catch");
} finally {
System.out.println("finally");
}
  • A. 例外が発生しなかった場合のみ
  • B. 例外が発生した場合のみ
  • C. catch ブロックが実行された場合のみ
  • D. 例外の有無にかかわらず常に実行される
解答と解説

正解:D

finally ブロックは try ブロックの処理が終わった後、例外の有無にかかわらず必ず実行されるSystem.exit() が呼ばれた場合などを除く)。

  • 例外なし:tryfinally
  • 例外あり・catch可能:trycatchfinally
  • 例外あり・catchなし:tryfinally → 例外が伝播

この特性から、ファイルやDB接続などのリソース解放に finally が使われてきた。現代では try-with-resources を使う方が安全で簡潔だ。


1-2. Optional<T> に関する記述として正しいものはどれか。

  • A. Optional.of(null) は空の Optional を返す
  • B. Optional.get() は値が空でも null を返す
  • C. Optional.orElse() は値が空の場合に指定したデフォルト値を返す
  • D. Optional はフィールドに持つことが推奨される
解答と解説

正解:C

  • A:Optional.of(null)NullPointerException をスローする。null を許容するなら Optional.ofNullable(null) を使う
  • B:Optional.get() は値が空なら NoSuchElementException をスローする
  • C:正しいopt.orElse("default") は値があればその値、空なら "default" を返す
  • D:Optional をフィールドに持つのはアンチパターン。シリアライズできず、設計の意図も伝わりにくい。フィールドは null で持ち、アクセサメソッドで Optional を返すのが推奨

1-3. 次のコードの問題として最も適切なものはどれか。

public void processUser(String name, int age) {
try {
if (name == null) throw new NullPointerException();
if (age < 0) throw new Exception("年齢が不正");
// 処理
} catch (Exception e) {
System.out.println("エラー: " + e.getMessage());
}
}
  • A. try-catch を使うべきでない
  • B. 引数バリデーションに例外を使うべきでなく、またキャッチする例外が広すぎる
  • C. Exception をキャッチすることは常に正しい
  • D. 問題はない
解答と解説

正解:B

問題点1:引数バリデーションに try 内で例外を生成するのは冗長

引数のバリデーションは try-catch なしでシンプルに書く:

if (name == null) throw new IllegalArgumentException("nameはnullにできません");
if (age < 0) throw new IllegalArgumentException("ageは0以上である必要があります");

問題点2:Exception を広くキャッチして無視している

catch (Exception e)NullPointerExceptionOutOfMemoryError 以外のほぼ全ての例外を捕捉する。意図しない例外も飲み込んでしまい、バグが見えなくなる。具体的な例外クラスでキャッチするか、エラーを適切に処理する。


1-4. try-with-resources の説明として正しいものはどれか。

  • A. try 内で生成したオブジェクトは全て自動クローズされる
  • B. AutoCloseable を実装したリソースを自動的にクローズする
  • C. finally ブロックの代わりに使う
  • D. Java 11以降でのみ使用できる
解答と解説

正解:B

  • A:try() 内で宣言されたもの(AutoCloseable 実装)のみ自動クローズされる
  • B:正しい() 内に AutoCloseable を実装したオブジェクトを宣言すると、try ブロック終了時に自動で close() が呼ばれる
  • C:finally が不要になるわけではなく、リソース管理に特化した構文
  • D:Java 7以降で使用可能
// AutoCloseable の実装例
class MyResource implements AutoCloseable {
@Override
public void close() {
System.out.println("リソースをクローズ");
}
}
try (MyResource r = new MyResource()) {
// 処理
} // ← r.close() が自動的に呼ばれる

2. 次のコードの( )に入るコードを答えよ。

// (1) 引数バリデーションに適切な例外を使う
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new (1)("年齢が不正な値です: " + age);
}
this.age = age;
}
// (2) Optional を使ってnullを安全に処理
public Optional<String> findEmail(int userId) {
String email = database.get(userId); // nullかもしれない
return Optional.(2)(email);
}
// (3) try-with-resources でファイルを読む
try (3) FileReader reader = new FileReader("data.txt") ){
// 処理
} catch (IOException e) {
e.printStackTrace();
}
// (4) Optional の値がある場合だけ処理を実行
Optional<String> name = Optional.of("Alice");
name.(4)(n -> System.out.println("Hello, " + n));
解答欄
解答と解説
  1. IllegalArgumentException
  2. ofNullable
  3. ((かっこの開き)
  4. ifPresent

解説

(1) 引数の不正チェックには IllegalArgumentExceptionjava.lang パッケージ、import不要)を使う。プログラムのバグを示す非検査例外であり、呼び出し元に誤った引数を渡していることを知らせる。

(2) Optional.ofNullable(value) は引数が null なら Optional.empty() を、そうでなければ Optional.of(value) を返す。null かもしれない値を Optional にラップする場合に使う。

(3) try-with-resources の構文は try (リソース宣言) { 処理 }() でリソースを囲む。

(4) ifPresent(Consumer) は Optional に値があれば Consumer を実行し、空なら何もしない。

3-1. 次のコードを、適切な例外処理とガード節を使ってリファクタリングせよ。

public String getUserEmail(int userId) {
if (userId > 0) {
User user = userRepository.findById(userId);
if (user != null) {
if (user.getEmail() != null) {
return user.getEmail();
} else {
return "メールアドレス未設定";
}
} else {
return "ユーザーが存在しません";
}
} else {
return "無効なユーザーID";
}
}
解答欄
解答と解説
public String getUserEmail(int userId) {
if (userId <= 0) throw new IllegalArgumentException("無効なユーザーID: " + userId);
User user = userRepository.findById(userId);
if (user == null) throw new UserNotFoundException(userId);
return Optional.ofNullable(user.getEmail())
.orElse("メールアドレス未設定");
}

改善点:

  1. ガード節:条件を反転させて早期リターン(例外スロー)することでネストを削減
  2. 不正な入力は例外でシグナルを送る:不正なIDは文字列で返すより例外の方が呼び出し元が適切に処理できる
  3. Optional の活用null の場合のデフォルト値は orElse で簡潔に表現

3-2. UserService クラスで「ユーザーが見つからない」場合の独自例外クラス UserNotFoundException を実装し、findById メソッドで使用するコードを書け。

解答欄
解答と解説
// 独自例外クラス
public class UserNotFoundException extends RuntimeException {
private final int userId;
public UserNotFoundException(int userId) {
super("ユーザーが見つかりません: ID=" + userId);
this.userId = userId;
}
public int getUserId() { return userId; }
}
// UserService での使用
public class UserService {
private final Map<Integer, User> userStore = new HashMap<>();
public User findById(int userId) {
User user = userStore.get(userId);
if (user == null) {
throw new UserNotFoundException(userId);
}
return user;
}
// Optional を返すバリエーション(nullを許容するAPIの場合)
public Optional<User> findByIdOptional(int userId) {
return Optional.ofNullable(userStore.get(userId));
}
}
// 呼び出し元
try {
User user = service.findById(999);
} catch (UserNotFoundException e) {
System.err.println(e.getMessage());
System.err.println("対象ID: " + e.getUserId());
}

独自例外に userId フィールドを持たせることで、エラーハンドリング時に「どのIDで失敗したか」を型安全に取り出せる。

下の Java WASM プレイグラウンドで Calculator を完成させ、例外変換と Optional の使い分けを確認せよ。

  1. divide でゼロ除算を明示的に検出し、ArithmeticException を投げる
  2. parseAndAddNumberFormatExceptionIllegalArgumentException に変換する
  3. safeDivide では例外ではなく Optional.empty() を返す
  4. main のテストケースを増やし、正常系と異常系の両方を観察する
Java WASM Playground
ブラウザ内で Java をコンパイルして実行します / 編集内容は自動保存されます / Ctrl+Enter でも実行できます
使い方: 1ファイルならそのまま編集できます。複数ファイルに分けたい場合は // File: Main.java のような行で区切ってください。
出力
「実行」を押すと、ここにコンパイル結果と実行結果が表示されます。
  • 「例外で止める」ケースと「Optional で表現する」ケースをどう使い分けるか
  • 例外メッセージに入力値を含めると調査しやすくなる理由
  • try-catch は必要な場所だけに絞るべき理由