Метод finalize, интерфейс closeable, try-with-resources(java7) - 1

— Привет, Амиго!

Я тут решила с тобой еще раз обсудить метод finalize().

Если ты помнишь, то finalize() – это специальный метод, который вызывается у объекта перед тем, как
сборщик мусора его уничтожит.

Основная цель этого метода – освободить используемые внешние не-Java ресурсы: закрыть файлы, потоки ввода-вывода и т.п.

К сожалению, этот метод не оправдывает возложенных на него надежд. Java-машина может отложить уничтожение объекта, как и вызов метода finalize на сколько угодно. Более того, она вообще не гарантирует, что этот метод будет вызван. В куче ситуаций ради «оптимизации» он не вызывается.

Приведу тебе две цитаты:

У Джошуа Блоха хорошо написано об этом методе: link
Краткая выдержка:

  1. finalize() можно использовать только в двух случаях:
    1. Проверка/подчистка ресурсов с логированием.
    2. При работе с нативным кодом, который не критичен к утечке ресурсов.
  2. finalize() замедляет работу GC по очистке объекта в 430 раз
  3. finalize() может быть не вызван
Если я на собеседовании скажу, что finalize — это вредный и опасный костыль, который своим существованием сбивает с толку, то буду прав?

— М-да, обрадовала ты меня, Элли.

— На замену метода finalize в Java 7 появилась новая конструкция. Называется она – try-with-resources. Это не совсем замена finalize – скорее альтернативный подход.

— Как try-catch, только с ресурсами?

— Почти как try-catch. Дело в том, что в отличие от метода finalize(), блок finally из конструкции try-catch-finally вызывается всегда. Этим и пользовались программисты, когда нужно было гарантированно освободить ресурсы, закрыть потоки и т.д.
Пример:

InputStream is = null;
try
{
 is = new FileInputStream("c:/file.txt");
 is.read(…);
}
finally
{
 if (is != null)
 is.close();
}

Независимо от того, нормально ли отработал блок try, или там возникло исключение, блок finally вызовется всегда, и там можно будет освободить занятые ресурсы.

Поэтому в Java 7 этот подход решили сделать официальным, и вот что из этого вышло:

try(InputStream is = new FileInputStream("c:/file.txt"))
{
 is.read(…);
}

Это специальная конструкция try, называемая try-with-resources (так же как и второй for для коллекций называется foreach).

Обрати внимание – после try следуют круглые скобки, где объявляются переменные и создаются объекты. Эти объекты можно использовать внутри блока try, обозначенного скобками {}. Когда выполнение команд блока try закончится, независимо от того – нормально оно закончилось или было исключение, для объекта, созданного внутри круглых скобок (), будет вызван метод close();

— Как интересно. Такая запись гораздо компактнее, чем предыдущая. Понять бы ее еще.

— Все не так сложно, как ты думаешь.

— А я могу указывать в круглых скобках объекты своих классов?

— Да, конечно, иначе от этих скобок было бы мало пользы.

— А если мне нужно вызвать другой метод при выходе из блока try, где мне его указать?

— Тут все немного тоньше. В Java 7 появился такой интерфейс:

public interface AutoCloseable
{
 void close() throws Exception;
}

Ты можешь унаследовать свой класс от такого интерфейса. И тогда его объекты можно будет использовать внутри try-with-resources. Только объекты такого типа можно использовать внутри круглых скобок try-with-resources для «автоматического закрытия».

— Т.е. мне нужно будет переопределить метод close и написать в нем код по «очистке» моего объекта, а указать другой метод нельзя?

— Ага. Зато можно указывать несколько объектов, разделив их точкой с запятой:

try(
InputStream is = new FileInputStream("c:/file.txt");
OutputStream os = new FileOutputStream("c:/output.txt")
)
{
 is.read(…);
 os.write(…);
}

— Уже лучше, но не так круто, как я надеялся.

— Все не так плохо, ты привыкнешь. Со временем.