Menu
Coddy logo textTech

Паттерн Try With Resources

Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 62 из 87.

При работе с такими ресурсами, как файловые потоки или подключения к базам данных, вы всегда должны закрывать их по завершении работы — в противном случае вы рискуете столкнуться с утечками памяти. Традиционный подход использует блок finally, но он многословен и подвержен ошибкам.

Оператор Java try-with-resources автоматически закрывает ресурсы по завершении блока. Любой класс, реализующий интерфейс AutoCloseable, может использоваться с этим синтаксисом:

// Традиционный подход — многословный и рискованный
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

Чтобы создать свой собственный автоматически закрываемый класс, реализуйте интерфейс 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! Вы создадите пользовательские автозакрываемые классы, имитирующие управление соединениями и сессиями, гарантируя, что они всегда будут правильно очищены.

Вы организуете свой код в трех файлах:

  • Connection.java: Создайте класс, имитирующий соединение с базой данных. Ваш класс Connection должен реализовывать интерфейс AutoCloseable. Ему требуется приватное поле для имени соединения (String), конструктор, который принимает имя и выводит [name] connection opened, метод query(String sql), который выводит Executing on [name]: [sql], и обязательный метод close(), который выводит [name] connection closed.
  • Session.java: Создайте еще один автозакрываемый класс, имитирующий пользовательскую сессию. Ваш класс Session также должен реализовывать AutoCloseable. Включите приватное поле для имени пользователя (String), конструктор, который принимает имя пользователя и выводит Session started for [username], метод performAction(String action), который выводит [username] performed: [action], и метод close(), который выводит Session ended for [username].
  • Main.java: Объедините вашу систему управления ресурсами! Вы получите три входных значения: имя соединения, имя пользователя и действие для выполнения.

    Сначала продемонстрируйте управление одиночным ресурсом. Выведите === Single Resource ===, затем используйте try-with-resources для создания Connection с заданным именем. Внутри блока try вызовите query("SELECT * FROM users"). Соединение должно автоматически закрыться по завершении блока.

    Затем продемонстрируйте работу с несколькими ресурсами. Выведите пустую строку, а затем === Multiple Resources ===. Используйте try-with-resources одновременно с Connection (используя то же имя) и Session (используя имя пользователя), объявленными в таком порядке через точку с запятой. Внутри блока try сначала вызовите query("INSERT INTO logs") у соединения, а затем вызовите performAction с входным действием у сессии. Посмотрите, как ресурсы закрываются в обратном порядке!

Вы получите три входных значения по порядку: имя соединения (String), имя пользователя (String) и действие (String).

Обратите внимание, как try-with-resources избавляет от необходимости явных вызовов close и блоков finally. Ресурсы гарантированно закроются, даже если возникнут исключения, а несколько ресурсов закрываются в порядке, обратном их объявлению!

Шпаргалка

Оператор try-with-resources автоматически закрывает ресурсы по завершении блока. Можно использовать любой класс, реализующий интерфейс AutoCloseable:

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

Чтобы создать собственный класс с поддержкой автоматического закрытия, реализуйте интерфейс AutoCloseable и его метод close():

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

Попробуйте сами

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: Внутри блока try вызовите query("SELECT * FROM users")
        
        // === Multiple Resources ===
        // TODO: Выведите пустую строку и заголовок
        // TODO: Используйте try-with-resources одновременно с Connection и Session
        // TODO: Внутри блока try:
        //       - Вызовите query("INSERT INTO logs") у объекта connection
        //       - Вызовите performAction с входным действием action у объекта session
        
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование