Java | 基礎文法:try

Java Java
スポンサーリンク

try の全体像

try は「失敗しうる処理を囲み、例外が起きたときに安全に対応する」ための構文です。基本は try の中に通常処理を書き、失敗したら catch で受け止め、最後の後始末を finally で必ず実行します。ファイルやソケットなど外部リソースを扱うときは、try-with-resources を使うと自動でクローズされ、例外時でも漏れません。


try-catch-finally の基本

例外を捕まえてメッセージを出す

try {
    int x = Integer.parseInt("123");
    System.out.println("OK: " + x);
} catch (NumberFormatException e) {
    System.err.println("数値に変換できません: " + e.getMessage());
}
Java

try の中で例外が発生すると、その型に一致する catch に処理が飛びます。catch は「何が起きたのか」を具体的に伝える役割で、復旧手段があるならそこで行います。

後始末は finally で必ず実行

java.io.BufferedReader br = null;
try {
    br = new java.io.BufferedReader(new java.io.FileReader("input.txt"));
    System.out.println(br.readLine());
} catch (java.io.IOException e) {
    System.err.println("読み込み失敗: " + e.getMessage());
} finally {
    if (br != null) try { br.close(); } catch (java.io.IOException ignored) {}
}
Java

finally は例外の有無に関わらず必ず実行されます。手動クローズが必要なリソースの「後始末」はここに置きます。


try-with-resources(自動クローズ)

最短で安全に後始末する

import java.nio.file.*;
import java.io.*;

public class ReadFirstLine {
    public static String firstLine(Path p) throws IOException {
        try (var br = Files.newBufferedReader(p)) {
            return br.readLine();
        } // ここで自動 close(例外時も必ず)
    }
}
Java

括弧内の変数は AutoCloseable を実装している必要があります。ファイル、ソケット、DB接続などはこれで管理すると、例外時でも確実にクローズされ、漏れがありません。

例外の抑制情報(サプレスト)に注意

try-with-resources では、クローズ時の例外が「サプレスト例外」として本体の例外に付与されます。ログやスタックトレースで「suppressed」の項目も確認すると原因特定が速くなります。


catch の順序と複数捕捉

具体的な例外から先に並べる

try {
    // I/O処理
} catch (java.io.FileNotFoundException e) {
    System.err.println("ファイルが存在しません");
} catch (java.io.IOException e) {
    System.err.println("I/O失敗");
}
Java

FileNotFoundException は IOException のサブクラスです。サブクラスを先に、親クラスを後に並べます。逆順だと到達不能でコンパイルエラーになります。

マルチキャッチで簡潔に書く

try {
    // 変換+I/O処理
} catch (NumberFormatException | java.io.IOException e) {
    System.err.println("入力エラー: " + e.getMessage());
}
Java

同じ対処でよい複数の例外は、| でまとめて捕捉できます。


throws と再スロー、例外の連鎖

ここでは処理せず、呼び出し側へ委ねる

void load(java.nio.file.Path p) throws java.io.IOException {
    try (var br = java.nio.file.Files.newBufferedReader(p)) {
        System.out.println(br.readLine());
    }
}
Java

回復をこの層で行わない判断なら、throws で上位へ伝えます。アプリの入口など一箇所にエラーハンドリングを集約すると、ログやユーザー通知が整理されます。

原因例外をつないで意味を保つ

try {
    service.process(req);
} catch (java.sql.SQLException e) {
    throw new IllegalStateException("DB処理失敗: reqId=" + req.id(), e);
}
Java

新しい例外へ包むときは、必ず原因(e)を渡して連鎖させます。根本原因のスタックトレースが失われないため、運用での解析が容易になります。


よくある落とし穴と安全な書き方(重要ポイントの深掘り)

例外の握り潰しは厳禁

何もしない catch は問題を隠します。少なくともログは出すか、ユーザー向けメッセージ・再スロー・既定動作のいずれかを設計します。

catch の範囲は必要最小限に

大きく try を囲み過ぎると、どこで失敗したかが曖昧になります。「失敗しうる最小の塊」を try にし、成功パスの読みやすさも保ちます。

例外で通常の分岐を作らない

例外は「想定外の異常」に使う道具です。通常の検証は if で前倒しに行い、契約違反は IllegalArgumentException などで明確に弾きます。

変数の初期化とスコープに注意

try の内外で使う変数は、外で宣言します。例外でスキップされたときに未初期化参照が起きないよう、代入と使用の位置関係を明確にします。

String line;
try (var br = java.nio.file.Files.newBufferedReader(java.nio.file.Path.of("a.txt"))) {
    line = br.readLine();
} catch (java.io.IOException e) {
    line = ""; // 既定値にフォールバック
}
System.out.println(line);
Java

例題で身につける

例 1: 入力を検証し、例外を減らす

public class Price {
    public static int taxed(int subtotal, double taxRate) {
        if (subtotal < 0) throw new IllegalArgumentException("subtotal must be >= 0");
        if (taxRate < 0 || taxRate > 1) throw new IllegalArgumentException("taxRate must be 0..1");
        return (int) Math.round(subtotal * (1 + taxRate));
    }
}
Java

通常の前提違反は if で弾き、例外は「本当に異常な外部要因」に集中させます。

例 2: ファイル読込とエラー通知の基本形

import java.nio.file.*;
import java.io.*;

public class Reader {
    static String readFirst(Path p) throws IOException {
        try (var br = Files.newBufferedReader(p)) {
            return br.readLine();
        }
    }
    public static void main(String[] args) {
        try {
            System.out.println(readFirst(Path.of("input.txt")));
        } catch (IOException e) {
            System.err.println("読込失敗: path=" + pToStringSafe(e));
            e.printStackTrace(); // 運用ログへ
        }
    }
    static String pToStringSafe(Exception e) { return e.getMessage(); }
}
Java

例 3: マルチキャッチでシンプルにフォールバック

public class ParseAndLoad {
    public static void main(String[] args) {
        try {
            int port = Integer.parseInt(System.getenv("APP_PORT"));
            java.net.ServerSocket server = new java.net.ServerSocket(port);
            System.out.println("start on " + port);
        } catch (NumberFormatException | java.io.IOException e) {
            System.err.println("設定不正または起動失敗: " + e.getMessage());
            System.out.println("既定ポート 8080 で再試行します");
        }
    }
}
Java

仕上げのアドバイス(重要部分のまとめ)

try は「失敗しうる最小の塊」を囲み、catch は具体的に対応し、finally(または try-with-resources)で必ず後始末するのが基本です。サブクラスの例外を先に並べ、同じ対処ならマルチキャッチで簡潔に。ログは一箇所で責任を持って出し、原因連鎖を保つ。通常の分岐は if で前倒しに検証し、例外は本当に異常な事態へ限定する。この型が身につくと、落ちても壊れないコードに変わります。

タイトルとURLをコピーしました