По моему опыту, есть одна и только одна причина переопределения Object.finalize()
, но это очень веская причина :
Чтобы разместить код регистрации ошибок, в finalize()
котором уведомляет вас, если вы когда-нибудь забудете вызвать close()
.
Статический анализ может выявлять пропуски только в сценариях тривиального использования, а предупреждения компилятора, упомянутые в другом ответе, имеют настолько упрощенное представление о вещах, что вам действительно нужно отключить их, чтобы выполнить что-то нетривиальное. (У меня включено намного больше предупреждений, чем у любого другого программиста, о котором я знаю или когда-либо слышал, но у меня не включены глупые предупреждения.)
Финализация может показаться хорошим механизмом для обеспечения того, чтобы ресурсы не оставались нераспределенными, но большинство людей воспринимают это совершенно неправильно: они думают об этом как о альтернативном резервном механизме, защите «второго шанса», которая автоматически спасет день, избавившись от ресурсов, которые они забыли. Это совершенно неправильно . Должен быть только один способ сделать любую вещь: либо вы всегда закрываете все, либо финализация всегда закрывает все. Но поскольку завершение ненадежно, оно не может быть завершением.
Итак, есть эта схема, которую я называю Mandatory Disposal , и она предусматривает, что программист всегда отвечает за явное закрытие всего, что реализует Closeable
или AutoCloseable
. (Оператор try-with-resources по-прежнему считается явным закрытием.) Конечно, программист может забыть, так что вот когда финализация вступает в игру, но не как волшебная фея, которая волшебным образом исправит ситуацию в конце: если финализация обнаружит это close()
не было вызвано, это непопытаться вызвать его именно потому, что (с математической уверенностью) найдутся орды программистов n00b, которые будут полагаться на него, чтобы выполнять работу, которую они слишком ленивы или слишком рассеянны. Таким образом, при обязательном удалении, когда финализация обнаруживает, что close()
не было вызвано, она записывает ярко-красное сообщение об ошибке, сообщая программисту большими жирными заглавными буквами, чтобы он исправил свои ошибки.
В качестве дополнительного преимущества, ходят слухи, что «JVM будет игнорировать тривиальный метод finalize () (например, метод, который просто возвращает, ничего не делая, как тот, который определен в классе Object)», поэтому при обязательном удалении вы можете избежать всей финализации издержки во всей вашей системе ( см. ответ alip для получения информации о том, насколько ужасны эти издержки), кодируя ваш finalize()
метод следующим образом:
@Override
protected void finalize() throws Throwable
{
if( Global.DEBUG && !closed )
{
Log.Error( "FORGOT TO CLOSE THIS!" );
}
//super.finalize(); see alip's comment on why this should not be invoked.
}
Идея заключается в том, что Global.DEBUG
это static final
переменная, значение которой известно во время компиляции, поэтому, если это так, false
то компилятор вообще не будет генерировать никакого кода для всего if
оператора, что сделает этот тривиальный (пустой) финализатор, который, в свою очередь, означает, что ваш класс будет обрабатываться так, как будто у него нет финализатора. (В C # это было бы сделано с хорошим #if DEBUG
блоком, но что мы можем сделать, это Java, где мы платим очевидную простоту в коде с дополнительными издержками в мозге.)
Подробнее о принудительном удалении, с дополнительным обсуждением об утилизации ресурсов в dot Net, здесь: michael.gr: Обязательное удаление против мерзости "избавляйся от утилизации"
finalize()
немного запутана. Если вы когда-либо реализуете его, убедитесь, что он поточно-ориентирован по отношению ко всем остальным методам того же объекта.