リソース自動クローズ(try-with-resources) — リソース漏れ防止
「使い終わったら必ず閉じる」を、言い忘れなく自動でやってくれる構文が try-with-resources。Java 7+で導入され、ファイル、ネットワーク、DB接続などのクローズ漏れを防ぎ、コードもすっきりします。
基本概念(AutoCloseable を自動で close)
- 対象:
AutoCloseable(例:Closeable、BufferedReader、InputStream、java.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 などのリソース管理はこれ一択。
- 抑制例外やクローズ順を理解しておくと、障害解析と安全性がさらに向上。
