Java入門 #20 - throws と例外の伝播

入門 | 10分 で読める | 2026.05.02

公式ドキュメント

この記事の要点

throws はメソッドが例外を投げうることを宣言する
throw は実際に例外オブジェクトを投げる
• 独自例外クラスを作ることで業務ロジックのエラーを型で表現できる

このシリーズについて

「Java入門」シリーズの第20回です。前回 #19 - 例外処理の基本 で try-catch を学びました。本記事では例外を呼び出し元に委譲する仕組みと、自分で例外を投げる方法を解説します。

前提条件: #19 - 例外処理の基本 で try-catch と checked / unchecked 例外の概念を理解していること。

throws とは

メソッドの中で checked 例外が発生する可能性があるとき、次の2つの選択肢があります。

  1. そのメソッド内で try-catch する
  2. メソッド宣言に throws を付けて、呼び出し元に処理を任せる
// Java 21 - メソッド内で try-catch する例
public void readFileA(String path) {
    try {
        FileReader reader = new FileReader(path); // IOException (checked)
        // 読み込み処理
    } catch (IOException e) {
        System.err.println("ファイルが開けませんでした: " + e.getMessage());
    }
}
// Java 21 - throws で呼び出し元に委譲する例
import java.io.FileReader;
import java.io.IOException;

public void readFileB(String path) throws IOException {
    FileReader reader = new FileReader(path); // try-catch 不要
    // 読み込み処理
}

2つ目の書き方では、readFileB を呼び出す側が try-catch するか、さらに上位に throws IOException を付けて連鎖させます。

ポイント: checked 例外をそのメソッドで回復できない場合、throws で上位に任せる方が自然です。最終的には main メソッドや上位レイヤで統一的にハンドリングします。

throw キーワード:例外を投げる

自分でエラー条件を判定して例外を投げるには throw を使います。

// Java 21 - 負数が渡されたら例外を投げる
public void setAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("年齢は0以上でなければなりません: " + age);
    }
    this.age = age;
}

throw の後ろには例外オブジェクトnew XXXException(...))を書きます。IllegalArgumentException は unchecked 例外なので、throws 宣言は不要です。

キーワード役割
throw実際に例外オブジェクトを投げる(処理中断)
throwsメソッド宣言に付け、例外が投げられる可能性を示す

注意: throw は**s なし**、throws は**s あり**です。綴りミスでコンパイルエラーになりやすいので注意しましょう。

throws / throw / try-catch の役割対比

構文使う場所役割
throws XxxExceptionメソッド宣言の末尾このメソッドが例外を投げうることを宣言(checked 例外は必須)
throw new XxxException()メソッド本体内実際に例外オブジェクトを生成して投げる
try-catchメソッド本体内例外を捕捉して処理する(上位に伝播させない)

checked 例外 vs unchecked 例外の使い分け

前回の復習も兼ねて、実務での使い分け方針を整理します。

種類継承元throws 宣言使うべき場面代表例
Checked ExceptionException だが RuntimeException 以外必須外部要因で予期可能なエラー(ファイルなし、ネットワーク切断)IOException, SQLException
Unchecked ExceptionRuntimeException任意(普通は書かない)プログラムのバグ、引数の前提条件違反NullPointerException, IllegalArgumentException

checked 例外は「こうなることがありうるから呼び出し側で対処してね」という設計意図を示します。unchecked 例外は「呼び出し方が間違っている」「ここに到達すること自体がバグ」という状況で投げます。

ポイント: 業務ロジックのエラー(「在庫が足りない」「権限がない」)を checked 例外にするか unchecked にするかは議論がありますが、近年は unchecked(RuntimeException 継承)にして、必要な箇所だけ try-catch する設計が好まれます。

自作例外クラス

Java 標準の例外だけでなく、独自の例外クラスを作ることができます。

// Java 21 - 独自の checked 例外
public class InsufficientStockException extends Exception {
    public InsufficientStockException(String message) {
        super(message);
    }
}
// Java 21 - 独自の unchecked 例外
public class InvalidUserIdException extends RuntimeException {
    public InvalidUserIdException(String message) {
        super(message);
    }
}

継承元を決める基準:

継承元いつ使うか
Exception呼び出し元に必ず try-catch または throws を強制したい
RuntimeException呼び出し元が任意にキャッチできれば良い

実務では RuntimeException を継承して、ドメインロジックの異常(OrderNotFoundExceptionPaymentFailedException 等)を表現するケースが増えています。

ヒント: 例外クラス名は必ず Exception で終わらせる慣例があります(例: UserNotFoundException)。これにより、コードを読んだときに例外だとすぐわかります。

実行例1:throws を使ったメソッド連鎖

// Java 21 - ファイル読み込みロジックを分離する例
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;

public class FileService {
    // throws で上位に委譲
    public String readFirstLine(String path) throws IOException {
        try (BufferedReader br = new BufferedReader(new FileReader(path))) {
            return br.readLine();
        }
    }

    public static void main(String[] args) {
        FileService service = new FileService();
        try {
            String line = service.readFirstLine("data.txt");
            System.out.println("最初の行: " + line);
        } catch (IOException e) {
            System.err.println("ファイル読み込み失敗: " + e.getMessage());
        }
    }
}

readFirstLinethrows IOException を宣言しているため、呼び出し元(main)で try-catch しています。

実行例2:独自例外を投げる

// Java 21 - 在庫チェックで独自例外を投げる例
public class StockService {
    private int stock = 10;

    public void reserve(int quantity) throws InsufficientStockException {
        if (quantity > stock) {
            throw new InsufficientStockException("在庫不足です。在庫: " + stock + ", 要求: " + quantity);
        }
        stock -= quantity;
        System.out.println(quantity + "個予約しました。残り在庫: " + stock);
    }

    public static void main(String[] args) {
        StockService service = new StockService();
        try {
            service.reserve(15); // 在庫10なので例外発生
        } catch (InsufficientStockException e) {
            System.err.println("エラー: " + e.getMessage());
        }
    }
}

実行結果(例):

エラー: 在庫不足です。在庫: 10, 要求: 15

InsufficientStockException をキャッチすることで、単なる Exception よりも具体的なエラー処理(在庫補充の通知など)が可能になります。

複数の例外を throws する

メソッドが複数種類の checked 例外を投げる可能性がある場合、カンマ区切りで列挙します。

// Java 21 - 複数の例外を宣言
import java.io.IOException;
import java.sql.SQLException;

public void processData(String filePath, String dbUrl) throws IOException, SQLException {
    // ファイル読み込み → IOException
    // DB接続 → SQLException
}

呼び出し側は両方をキャッチするか、さらに上位に throws します。

注意: throws で宣言する例外が増えすぎると、呼び出し元が複雑になります。関連する例外を親クラスでまとめるか、独自の unchecked 例外でラップする設計も検討しましょう。

プログラミングのベストプラクティス

やるべきこと理由
checked 例外は本当に回復可能なときだけ呼び出し側に無意味な try-catch を強制しない
例外メッセージは具体的に「エラーが発生しました」だけでは原因不明
ログに残してから再スロー上位で握り潰されても痕跡が残る
例外を catch して無視しない空の catch ブロックは障害の温床

次のステップ

Java入門 #21 - 継承(extends)の基本 では、クラスの継承と super() でスーパークラスのコンストラクタを呼び出す方法を学びます。例外クラスの継承も同じ仕組みです。

参考リソース

この技術を体系的に学びたいですか?

未来学では東証プライム上場企業のITエンジニアが24時間サポート。月額24,800円から、退会金0円のオンラインIT塾です。

メールで無料相談する
← 一覧に戻る