Почему метод finalize() в java.lang.Object "защищен"?
Из любопытства,
Почему finalize()
модификатор доступа метода сделан как protected
, Почему это не может быть public
? Может кто-нибудь объяснить мне какую-либо конкретную причину этого?
Кроме того, я узнал, что finalize()
метод вызывается только один раз. Если я вызову это дважды в своей программе, что происходит? Будет ли сборщик мусора вызывать это снова?
private void dummyCall() {
try {
finalize();
finalize();
} catch (Throwable e) {
e.printStackTrace();//NOT REACHES EXCEPTION
}
}
9 ответов
Я отвечаю на ваш вопрос другим вопросом:
Зачем
finalize
метод не должен быть защищен?
В общем, вы должны стараться держать вещи как можно более приватными. Вот что такое инкапсуляция. В противном случае вы могли бы сделать все public
, finalize
не может быть private
(поскольку производные классы должны иметь возможность доступа к нему, чтобы иметь возможность переопределять его), поэтому он должен по крайней мере protected
но зачем выдавать больше доступа, когда это нежелательно?
После более внимательного прочтения вашего комментария, я полагаю, что теперь я понимаю вашу главную мысль. Я думаю, что ваша точка зрения так как все вытекает из java.lang.Object
и, следовательно, получает доступ к его protected
члены, это не будет иметь никакого значения для этого (или любой метод в java.lang.Object
в этом отношении) быть public
в отличие от protected
, Лично я бы посчитал это недостатком дизайна в Java. Это действительно исправлено в C#. Проблема не в том, почему finalize
защищен Это нормально. Реальная проблема заключается в том, что вы не сможете вызывать защищенные методы в базовом классе через ссылку на объект типа базового класса. У Эрика Липперта есть запись в блоге, в которой обсуждается, почему предоставление такого доступа защищенным членам является плохой идеей, которая более подробно рассматривается в этом вопросе о переполнении стека.
Почему модификатор доступа метода finalize() сделан защищенным. Почему это не может быть публичным?
Он не является общедоступным, поскольку не должен вызываться кем-либо, кроме JVM. Однако он должен быть защищен, чтобы его могли переопределить подклассы, которым необходимо определить поведение для него.
Если я вызываю это дважды в моей программе, что происходит внутри?
Вы можете вызывать все, что хотите, в конце концов, это просто метод. Тем не менее, очень похоже public static void main(String [] args)
Это имеет особое значение для JVM
Будет ли сборщик мусора вызывать это снова?
да
- finalize предназначен для вызова только gc и поэтому не требует публичного доступа
- finalize гарантированно будет вызываться gc только один раз, вызов его сам нарушит эту гарантию, так как gc не будет знать об этом.
- Любой переопределяющий класс может сделать финал публичным, что я считаю плохим по вышеуказанным причинам.
- finalize не должен содержать много кода, так как любое исключение, выданное finalize, может уничтожить поток финализатора gc.
Rant против финализировать ()
- Управление собственными ресурсами или любыми ресурсами, требующими вызова dispose() или close(), может привести к трудностям при поиске ошибок, так как они будут выпущены только тогда, когда jvm не хватит памяти, вы должны освободить ресурсы вручную. Finalize следует использовать только для отладки утечек ресурсов или в тех случаях, когда ручное управление ресурсами - это слишком много работы.
- finalize будет вызываться в дополнительном потоке gc и может вызвать проблемы с блокировкой ресурса и так далее.
- ссылочные классы, такие как WeakReference и ReferenceQueue, представляют собой альтернативный (довольно сложный) способ очистки и могут иметь те же проблемы, что и finalize() для собственных ресурсов.
Остерегайтесь ошибок в вышеприведенных высказываниях, я немного устал:-)
Проверьте эту ссылку, которая обсуждает это.
По сути, было бы разумнее private
, так как он должен вызываться только JVM (сборщик мусора). Но для того, чтобы позволить подклассу вызвать родителя finalize()
метод как часть его finalize()
, должно быть protected
,
(Правка - и просто общее предостережение - использование метода finalize(), как правило, не рекомендуется, поскольку нет способа гарантировать, что он когда-либо будет вызван. Хотя это не означает, что у вас никогда не будет повода использовать его - это просто редко.)
Часть о finalize()
вызов только один раз относится только к вызовам из GC. Вы можете представить объект как имеющий скрытый флаг "finalize()
был вызван GC", и GC проверил этот флаг, чтобы узнать, что делать с объектом. Этот флаг никак не зависит от ваших собственных ручных вызовов finalize()
,
По завершении прочитайте эту статью от Ханса Бёма (который хорошо известен своей работой по сбору мусора). Это откровение о завершении; в частности, Бём объясняет, почему завершение обязательно асинхронно. Следствием этого является то, что, хотя финализация является мощным инструментом, он очень редко является подходящим инструментом для данной работы.
Это не public
(или доступ по умолчанию), потому что он должен вызываться JVM изнутри, когда объект собирается сборщиком мусора - он не должен вызываться ничем другим. И это не private
потому что он предназначен для переопределения, и вы не можете переопределить частные методы.
Если я вызываю это дважды в моей программе, что происходит внутри? Будет ли сборщик мусора вызывать это снова?
Возможно, да, но трудно представить сценарий, в котором это имело бы какой-то смысл - точка finalize()
это сделать очистку, когда объект собирается мусором. И это даже не так хорошо, так что это действительно то, что вы должны избегать, а не экспериментировать.
Также я узнал, что метод finalize() вызывается только один раз. Если я вызываю это дважды в моей программе, что происходит внутри?
Вы, вероятно, спросите об этом под впечатлением от деструкторов C++. В java метод finalize() не делает никакой магии (например, очистка памяти). Он должен вызываться сборщиком мусора. Но не наоборот.
Я рекомендую вам прочитать соответствующую главу в "Эффективной Java" Джошуа Блоха. В нем говорится, что использование финализаторов является плохой практикой и может вызвать проблемы с производительностью и другие проблемы, и есть только несколько случаев, когда их следует использовать. Глава начинается со следующих слов:
Финализаторы непредсказуемы, часто опасны и, как правило, не нужны.
finalize() используется только JVM для очистки ресурсов, когда объект собирается. Разумно, чтобы класс определил, какие действия следует предпринять в коллекции, для чего ему может потребоваться доступ к super.finalize(). Для внешнего процесса не имеет смысла вызывать finalize(), поскольку внешний процесс не контролирует, когда объект собирается.
Я думаю причина почему finalize
Защищено было бы то, что, возможно, он переопределяется некоторыми классами в JDK, и эти переопределенные методы вызываются JVM.