Как в Java автоматически закрыть ресурсы (например, потоки или соединения)?

Ответ

Начиная с Java 7, для этого используется конструкция try-with-resources. Она автоматически закрывает любые ресурсы, реализующие интерфейсы AutoCloseable или Closeable.

Синтаксис и пример:

try (FileInputStream fis = new FileInputStream("file.txt");
     BufferedInputStream bis = new BufferedInputStream(fis)) {
    // Работа с ресурсами
    int data = bis.read();
} // Здесь fis и bis будут автоматически закрыты, даже если выброшено исключение

Как это работает и почему это лучше:

  1. Автоматическое закрытие: Компилятор генерирует блок finally, который вызывает close() для каждого объявленного ресурса в обратном порядке.
  2. Подавление исключений: Если в блоке try и в close() возникли исключения, исключение из try будет основным, а из close() — подавленным (доступно через Throwable.getSuppressed()).
  3. Сравнение с ручным управлением (до Java 7):
    // Старый, многословный и подверженный ошибкам способ
    FileInputStream fis = null;
    try {
        fis = new FileInputStream("file.txt");
        // работа...
    } finally {
        if (fis != null) {
            try {
                fis.close(); // Может тоже выбросить исключение
            } catch (IOException e) {
                // Логирование, часто игнорируется
            }
        }
    }

Практическое правило: Всегда предпочитайте try-with-resources ручному закрытию в finally. Это делает код чище, безопаснее и предотвращает утечки ресурсов.

Ответ 18+ 🔞

А, ну вот, смотри, блядь, с седьмой джавы подвезли такую штуку — try-with-resources. Это чтоб твои ресурсы, которые AutoCloseable или Closeable, сами закрывались, как только ты с ними закончил. Не надо больше эти ебучки с finally и проверками на null писать, а то забудешь — и привет, утечка.

Вот как это выглядит, ёпта:

try (FileInputStream fis = new FileInputStream("file.txt");
     BufferedInputStream bis = new BufferedInputStream(fis)) {
    // Тут работаешь с ними, как обычно
    int data = bis.read();
} // А тут — магия! fis и bis закроются сами, даже если всё полетело в пизду с исключением.

А теперь, сука, почему это овердохуище удобнее:

  1. Закрывается само. Компилятор, хитрая жопа, сам сгенерирует невидимый finally, который вызовет close() на всех ресурсах, да ещё и в правильном порядке — с конца.
  2. Исключения не теряются. Если и в основном коде, и при закрытии что-то пошло не так, то исключение из try будет главным, а то, что из close(), — прицепом (достать его можно через Throwable.getSuppressed()). Раньше же второе просто затирало первое, и ты нихуя не понимал, где изначально косяк.
  3. Сравни с тем, как было раньше, до Java 7, ебать мои старые костыли:
    // Старый способ — много кода, и всё можно запороть
    FileInputStream fis = null;
    try {
        fis = new FileInputStream("file.txt");
        // работа...
    } finally {
        if (fis != null) {
            try {
                fis.close(); // А эта строка тоже может выкинуть исключение, блядь!
            } catch (IOException e) {
                // И его обычно просто забивали, в рот меня чих-пых
            }
        }
    }

Так что золотое правило, блядь: всегда юзай try-with-resources, если ресурс его поддерживает. Код в разы чище, надёжнее, и не придётся потом гадать, почему у тебя файловые дескрипторы кончились.