Menu
Coddy logo textTech

Try-With-Resources パターン

CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 62/87。

ファイルストリームやデータベース接続などのリソースを扱うときは、使い終わったら必ず閉じる必要があります。そうしないと、メモリリークが発生するおそれがあります。従来の approach では finally ブロックを使用しますが、冗長でエラーが発生しやすい方法です。

Java の try-with-resources 文は、AutoCloseable インターフェースを implements する任意の class をこの構文で使用でき、block が完了すると resources を自動的に閉じます。

// 従来のアプローチ - 冗長でリスクがある
BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("file.txt"));
    String line = reader.readLine();
} finally {
    if (reader != null) {
        reader.close();  // 例外をスローする可能性もあります!
    }
}

// Try-with-resources - クリーンで安全
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    String line = reader.readLine();
}  // ここで自動的にクローズされます

複数のリソースは、セミコロンで区切って管理できます。リソースは宣言した順序とは逆の順序で閉じられます。

try (FileReader fr = new FileReader("input.txt");
     BufferedReader br = new BufferedReader(fr)) {
    // 両方のリソースを使用する
}  // br が先に閉じられ、次に fr

独自の自動的にクローズ可能な class を作成するには、AutoCloseable インターフェースと、その単一の close() メソッドを実装します。

public class DatabaseConnection implements AutoCloseable {
    public DatabaseConnection() {
        System.out.println("Connection opened");
    }
    
    @Override
    public void close() {
        System.out.println("Connection closed");
    }
}

このパターンにより、処理中に例外が発生した場合でも、リソースが常に適切に解放されることが保証されます。

challenge icon

チャレンジ

簡単

try-with-resourcesの力を実証するリソース管理システムを構築しましょう!接続とセッションの管理をシミュレートするカスタムの自動クローズ可能な class を作成し、常に適切にクリーンアップされるようにします。

コードを3つのファイルに分けて整理します。

  • Connection.java: データベース接続をシミュレートする class を Create します。Connection class は AutoCloseable を implements する必要があります。接続名(String)用の private field、名前を受け取って [name] connection opened を出力する constructor、Executing on [name]: [sql] を出力する query(String sql) method、そして [name] connection closed を出力する必須の close() method が必要です。
  • Session.java: ユーザーセッションをシミュレートする、もう1つの自動クローズ可能な class を Create します。Session class も AutoCloseable を implements する必要があります。ユーザー名(String)用の private field、ユーザー名を受け取って Session started for [username] を出力する constructor、[username] performed: [action] を出力する performAction(String action) method、そして Session ended for [username] を出力する close() method を含めます。
  • Main.java: リソース管理システムをまとめます!3つの入力、つまり接続名、ユーザー名、実行する action を受け取ります。

    まず、単一リソースの管理を示します。=== Single Resource === を出力し、try-with-resources を使って、指定された名前で Connection を作成します。try ブロックの Inside で query("SELECT * FROM users") を call します。ブロックが終了すると、接続は自動的に close されます。

    次に、Multiple リソースを示します。blank line を出力してから、=== Multiple Resources === を出力します。同じ名前を使う Connection と、ユーザー名を使う Session の both を、セミコロンで区切り、その順序で宣言して try-with-resources を使用します。try ブロックの Inside では、first に接続で query("INSERT INTO logs") を call し、次にセッションで入力された action を指定して performAction を call します。リソースが reverse order で close される様子を確認しましょう!

3つの入力を順番に受け取ります。接続名(String)、ユーザー名(String)、action(String)です。

try-with-resources によって、明示的な close の call や finally ブロックが不要になることに注目してください。exception が発生した場合でもリソースは確実に close され、Multiple リソースは宣言順序の reverse order で close されます!

自分で試してみよう

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String connectionName = scanner.nextLine();
        String username = scanner.nextLine();
        String action = scanner.nextLine();
        
        // === Single Resource ===
        // TODO: ヘッダーを出力する
        // TODO: try-with-resources を使用して Connection を作成する
        // TODO: Inside the try block, call query("SELECT * FROM users")
        
        // === Multiple Resources ===
        // TODO: 空行とヘッダーを出力する
        // TODO: Connection と Session の両方で try-with-resources を使用する
        // TODO: try ブロック内で:
        //       - Call query("INSERT INTO logs") on the connection
        //       - セッションで入力 action を使って performAction を呼び出す
        
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Javaオンラインコンパイラ