Java 逆引き集 | リソース自動クローズ(try-with-resources) — リソース漏れ防止

Java Java
スポンサーリンク

リソース自動クローズ(try-with-resources) — リソース漏れ防止

「使い終わったら必ず閉じる」を、言い忘れなく自動でやってくれる構文が try-with-resources。Java 7+で導入され、ファイル、ネットワーク、DB接続などのクローズ漏れを防ぎ、コードもすっきりします。


基本概念(AutoCloseable を自動で close)

  • 対象: AutoCloseable(例:CloseableBufferedReaderInputStreamjava.sql.Connection/Statement/ResultSet)。
  • 仕組み: try の頭で宣言したリソースは、ブロックを抜けると自動で close() が呼ばれる。例外が起きても確実にクローズ。
import java.io.*;

public class ReadFirstLine {
    public static String readFirstLine(String path) throws IOException {
        try (BufferedReader br = new BufferedReader(new FileReader(path))) {
            return br.readLine();
        } // ここで br.close() が自動実行
    }
}
Java
  • ポイント: finally での手動 close が不要。漏れや二重 try-catch が消える。

使い方の定番(ファイル・複数リソース・JDBC)

ファイルの読み書き

try (var in = new BufferedReader(new FileReader("in.txt"));
     var out = new BufferedWriter(new FileWriter("out.txt"))) {
    String line;
    while ((line = in.readLine()) != null) {
        out.write(line);
        out.newLine();
    }
} // out → in の順で自動 close(宣言の逆順)
Java
  • ポイント: 複数リソースは「宣言の逆順」でクローズされる。依存関係が自然に安全。

JDBC(Connection/PreparedStatement/ResultSet)

import java.sql.*;

try (Connection con = DriverManager.getConnection(url, user, pass);
     PreparedStatement ps = con.prepareStatement("SELECT id,name FROM users WHERE id=?");
     ) {
    ps.setInt(1, 10);
    try (ResultSet rs = ps.executeQuery()) {
        if (rs.next()) {
            System.out.println(rs.getInt("id") + ":" + rs.getString("name"));
        }
    }
} // rs → ps → con の順で安全に close
Java
  • ポイント: ネストで ResultSet を囲うと、クローズ順が明確で例外にも強い。

Java 9 の拡張(外部変数の利用)

  • 外部で確保した final(または実質 final)なリソースを、try に“参照”として渡せる。
BufferedReader br = new BufferedReader(new FileReader("data.txt")); // final/実質final
try (br) { // Java 9+
    System.out.println(br.readLine());
} // br.close()
Java
  • 効果: リソースの生存期間を柔軟に設計できる(準備と使用の分離)。

例外と抑制例外(getSuppressed)

  • 抑制例外: try 本体で例外発生、さらに close() でも例外が出た場合、close 側の例外は「抑制例外」としてメイン例外にぶら下がる。
try (BadResource r = new BadResource()) {
    r.work(); // ここで例外
} catch (Exception e) {
    for (Throwable sup : e.getSuppressed()) {
        System.err.println("suppressed: " + sup);
    }
}
Java
  • ポイント: メインの失敗(本体)を優先しつつ、クローズ時の失敗も失われない。障害解析で有用。

カスタムリソース(AutoCloseable 実装)

class TimerScope implements AutoCloseable {
    private final long start = System.nanoTime();
    public void close() {
        long ms = (System.nanoTime() - start) / 1_000_000;
        System.out.println("elapsed = " + ms + " ms");
    }
}

try (var t = new TimerScope()) {
    // 計測したい処理
} // 自動で計測結果を出力
Java
  • 使い所: 一時的なロック、メトリクス、トランザクション風制御の「開始/終了」を自動化。

例題で身につける

例題1: CSV を読み込んで件数を数える

int count = 0;
try (var br = new BufferedReader(new FileReader("users.csv"))) {
    while (br.readLine() != null) count++;
}
System.out.println("件数=" + count);
Java
  • 狙い: 典型的なリソース利用を最短で安全に。

例題2: テキストをフィルタして別ファイルに出力

try (var in = new BufferedReader(new FileReader("log.txt"));
     var out = new BufferedWriter(new FileWriter("warn.txt"))) {
    String line;
    while ((line = in.readLine()) != null) {
        if (line.startsWith("WARN")) {
            out.write(line);
            out.newLine();
        }
    }
}
Java
  • 狙い: 複数リソースの宣言で、漏れなく確実にクローズ。

例題3: DB 読み込みと変換のパイプライン

try (var con = DriverManager.getConnection(url, user, pass);
     var ps = con.prepareStatement("SELECT price FROM items");
     var rs = ps.executeQuery()) {

    long total = 0;
    while (rs.next()) total += rs.getInt("price");
    System.out.println("合計=" + total);
}
Java
  • 狙い: JDBC では try-with-resources が事実上の標準形。

すぐ使えるテンプレート

  • 単一リソース
try (Type res = new Type(...)) {
    // use res
} // res.close()
Java
  • 複数リソース
try (TypeA a = ...; TypeB b = ...) {
    // use a, b
} // b → a の順に close
Java
  • Java 9+ 外部変数
Type res = ...; // final/実質final
try (res) {
    // use res
}
Java
  • 抑制例外の確認
try (...) { ... } catch (Exception e) {
    for (var s : e.getSuppressed()) { /* ログ */ }
}
Java

実務のコツと落とし穴

  • 必ず AutoCloseable: try-with-resources で使う型が AutoCloseable を実装しているか確認。
  • クローズ順を意識: 依存関係のあるリソースは「宣言順」を適切に。クローズは逆順。
  • 例外の握りつぶし禁止: catch で何もせず破棄は避ける。最低限ログに残す。
  • 巨大 try を避ける: リソース利用のスコープを狭く。処理をメソッド分割し、読みやすさを保つ。
  • finally との違い: finally での手動 close は例外の入れ子や null チェックが冗長。基本は try-with-resources を選択。

まとめ

  • try-with-resources は「確実な close」と「簡潔なコード」を同時に満たす標準構文。
  • ファイル・ネットワーク・DB などのリソース管理はこれ一択。
  • 抑制例外やクローズ順を理解しておくと、障害解析と安全性がさらに向上。

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