Java try-with-resources 文
try-with-resources は Java 7 で導入された簡潔なリソース管理方式であり、使用後に自動的に閉じる必要があるリソース(ファイル、データベース接続、ネットワーク接続など)に適しています。
try-with-resources は try-catch 文ブロック内で使用するリソースを簡単に閉じることができます。いわゆるリソース(resource)とは、プログラムの完了後に閉じる必要があるオブジェクトを指します。
try-with-resources 文は、各リソースが文の終了時に閉じられることを保証します。
java.lang.AutoCloseable インターフェース(java.io.Closeable を実装したすべてのオブジェクトを含む)を実装したすべてのオブジェクトは、リソースとして使用できます。
利点:
- コードの簡素化:リソースを手動で閉じるコードが不要になり、ロジックがより明確になります。
- エラーの削減:リソースのクローズを自動処理し、リソースの閉じ忘れや
finallyブロック内で発生する例外の処理漏れを防ぎます。 - パフォーマンスの向上:リソースリークを減らし、システムリソースを節約します。
実行フロー
try-with-resources を理解する核心は、リソースのライフサイクルと、例外発生時の制御フローの流れを把握することにあります。
基本構文
try-with-resources の構文は次のとおりです:
try (ResourceType resource = new ResourceType()) {
// 使用资源
} catch (ExceptionType e) {
// 处理异常
}
try ブロック内で宣言されたリソースは、コードの実行完了後に自動的に閉じられ、例外が発生した場合でも安全に閉じられます。
複数リソース管理(宣言とは逆順でクローズ):
try (Connection conn = DriverManager.getConnection(url);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
System.out.println(rs.getString(1));
}
} catch (SQLException e) {
e.printStackTrace();
}
クローズ順:
rs → stmt → conn(声明顺序的逆序)
クローズ順:複数のリソースは宣言順の逆順で閉じられます。つまり、後で宣言されたリソースが最初に閉じられます。これはスタック式(LIFO)管理と一致し、依存関係のあるリソースが正しく解放されることを保証します。
AutoCloseable インターフェースの使用
try-with-resources で管理したい任意のカスタムクラスは、java.lang.AutoCloseable(または java.io.Closeable)インターフェースを実装し、その close() メソッドを実装するだけで済みます。
例
private final String url;
public DatabaseConnection(String url) {
this.url = url;
System.out.println("接続が確立されました: " + url);
}
public void query(String sql) {
System.out.println("クエリを実行: " + sql);
}
@Override
public void close() {
System.out.println("接続が閉じられました: " + url);
// 基盤となる接続リソースを解放...
}
}
// 使用
try (DatabaseConnection db = new DatabaseConnection("jdbc:mysql://localhost/mydb")) {
db.query("SELECT * FROM users");
} catch (Exception e) {
e.printStackTrace();
}
出力:
连接已建立: jdbc:mysql://localhost/mydb 执行查询: SELECT * FROM users 连接已关闭: jdbc:mysql://localhost/mydb
例外処理と抑制例外
try ブロック内のビジネスロジックと close() が同時に例外をスローした場合、try-with-resources は特別な戦略を採用します。ビジネス例外は主例外として上位に伝播し、close() で発生した例外は抑制(Suppressed)され、主例外に付加されます。getSuppressed() で取得できます。
例
res.doSomething(); // 例外 A をスロー(ビジネス例外)
} catch (Exception e) {
System.out.println("主例外: " + e.getMessage());
for (Throwable s : e.getSuppressed()) { // 例外 B を取得
System.out.println("抑制例外: " + s.getMessage());
}
}
出力:
主异常: Exception in doSomething 抑制异常: Exception in close
注意:従来の try-finally では、finally ブロックの例外がビジネス例外を上書きし、元のエラーが失われる原因となります。try-with-resources は抑制例外メカニズムによってこの古典的な落とし穴を解決しました。
Java 9 拡張:外部変数の再利用
Java 9 より前では、try-with-resources はリソースを try() 内で再宣言する必要がありました。
Java 9 でこの制限が緩和されました。変数が effectively final(事実上の定数)であれば、try() 内で直接参照でき、再宣言は不要です。
Java 7 / 8 — 再宣言が必要
try (BufferedReader br1 = br) { // ← 新しい変数 br1 を宣言する必要がある
System.out.println(br1.readLine());
}
Java 9+ — 直接再利用
try (br) { // ← br を直接使用、再宣言不要
System.out.println(br.readLine());
}
effectively final:変数は初期化後に再度代入されていない場合、コンパイラは自動的に final とみなします。final キーワードを明示的に書く必要はありません。
try-finally との比較
| 観点 | try-finally | try-with-resources |
|---|---|---|
| コード量 | 多い——close() の手動呼び出しが必要 | 少ない——自動クローズ |
| close 漏れのリスク | 高い——人的ミスでリソースリークが発生しやすい | ゼロ——コンパイラが保証 |
| 複数リソースのネスト | ネストが深く、可読性が低い | セミコロン区切りで、フラットで明確 |
| 例外処理 | finally の例外がビジネス例外を上書き | 抑制例外メカニズムで、情報が完全に保持される |
| 適用範囲 | 任意のクリーンアップロジック | AutoCloseable を実装したリソース |
try-finally の書き方(冗長でエラーが発生しやすい)
try {
br = new BufferedReader(new FileReader("data.txt"));
String line;
while ((line = br.readLine()) != null) System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) { // さらに null チェックも必要
try { br.close(); } // close 自体も例外をスローする可能性がある
catch (IOException e) { e.printStackTrace(); }
}
}
try-with-resources の書き方(簡潔で安全)
String line;
while ((line = br.readLine()) != null) System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
一般的な使用シーン
| 分類 | 一般的なクラス/インターフェース |
|---|---|
| 📁 ファイル I/O | FileInputStream、FileOutputStream、BufferedReader、BufferedWriter、FileChannel |
| 🗄 データベース | Connection、PreparedStatement、Statement、ResultSet、EntityManager |
| 🌐 ネットワーク通信 | Socket、ServerSocket、HttpURLConnection、InputStream、OutputStream |
明示的にクローズが必要なリソースを扱う場合は、try-finally ではなく try-with-resources を優先的に使用すべきです。コンパイラがリソースのクローズを保証し、コードがより簡潔で、例外情報がより完全になります。これは Java リソース管理のベストプラクティスです。
その他の拡張