Javaのtry-with-resourcesをマスターする:自動リソース管理の仕組みと活用法
Javaのtry-with-resourcesをマスターする
Javaのtry-with-resources文がリソース管理をどう簡素化し、リソースリークを防ぎ、コードの可読性を高めるかを解説します。
try-with-resources構文を使うと、その括弧内で宣言されたすべてのリソースが、ブロックを正常に抜ける場合も例外で抜ける場合も、自動的にクローズされます。ここでいうリソースとは、AutoCloseableまたはCloseableを実装したオブジェクトのことです。
構文は以下の通りです:
try (ResourceType resource = new ResourceType(...)) { // リソースを使用 } catch (ExceptionType e) { // 例外を処理 }
使い方はシンプルです:
try節の中でリソースをインスタンス化する- 使用時やクローズ時に発生しうる例外をキャッチする
注意:AutoCloseableを実装したオブジェクトのみが自動管理の対象になります。
例1:ファイルを安全に読み込む
import java.io.*; public class Main { public static void main(String[] args) { try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) { String line; while ((line = br.readLine()) != null) { System.out.println("Line =>" + line); } } catch (IOException e) { System.out.println("IOException in try‑with‑resources block =>" + e.getMessage()); } } }
test.txtが存在しない場合の出力:
IOException in try‑with‑resources block =>test.txt (No such file or directory)
ファイルが存在する場合の出力:
Line =>test line
ここではBufferedReaderがブロック終了後に自動的にクローズされるため、finally節が不要になります。
抑制例外(Suppressed Exceptions)
tryブロックとリソースのクローズの両方で例外が発生した場合、tryブロックの例外が優先され、もう一方は抑制されます。抑制された例外はThrowable.getSuppressed()で取得できます:
catch (IOException e) { System.out.println("Thrown exception =>" + e.getMessage()); for (Throwable t : e.getSuppressed()) { System.out.println("Suppressed exception =>" + t); } }
try-with-resourcesを使う理由
1. 手動クリーンアップが不要になる
Java 7以前は、リソースをクローズするためにfinallyブロックが必須でした。これによりボイラープレートが増え、クローズ自体が例外を投げる場合はfinally内でさらにネストしたtry-catchが必要でした。try-with-resourcesならクリーンアップは暗黙的になります:
BufferedReader br = null; try { br = new BufferedReader(new FileReader("test.txt")); // brを使用 } catch (IOException e) { // 例外処理 } finally { if (br != null) { try { br.close(); } catch (IOException ignored) {} } }
上記の冗長でミスを招きやすいパターンが、以下のように簡潔になります:
try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) { // brを使用 }
2. 複数リソースを1文で宣言可能
セミコロンで区切って複数のリソースを宣言できます。宣言の逆順でクローズされます。
try (Scanner scanner = new Scanner(new File("testRead.txt")); PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) { while (scanner.hasNext()) { writer.print(scanner.nextLine()); } }
3. Java 9での拡張
Java 9では、リソースをローカル変数として宣言する必須要件が緩和されました。既存の変数を再利用できます:
Scanner scanner = new Scanner(new File("testRead.txt")); try (scanner) { // コード }
同じリソースを他でも使う場合に可読性が向上します。
詳細については、公式のAutoCloseableドキュメントとJava 9 JEP 326リリースノートを参照してください。
Java
- Java演算子をマスター:種類、構文、実践例
- Javaコメント:種類、使い方、ベストプラクティス
- Java if…elseをマスター:制御フローを徹底解説
- Java拡張forループをマスター:構文、例、ベストプラクティス
- Java break文:使い方、ラベル付きbreak、ネストループでの制御
- Java文字列をマスター:生成、メソッド、ベストプラクティス
- Javaインターフェース完全ガイド:概念、実装、ベストプラクティス
- Java無名インナークラス完全ガイド:定義、構文、実践例
- Javaアノテーション完全ガイド:種類、配置場所、実践例
- Javaアサーション完全ガイド:バグ早期発見とコード信頼性の向上
- Java Vectorの理解:使い方、特徴、ArrayListとの比較