Жизненный цикл объекта в Java это путь от создания до удаления: объект создается оператором new, живет и используется, пока на него есть хотя бы одна ссылка, а когда ссылок не остается, становится «мусором» и автоматически удаляется сборщиком мусора (Garbage Collector), который освобождает занятую память.
Привет! Думаю, ты не сильно удивишься, если тебе сказать, что размер памяти на твоем компьютере ограничен:)
Кратко
Жизненный цикл объекта: создание → жизнь (пока есть ссылки) → превращение в мусор → удаление сборщиком мусора.
При создании (new) JVM выделяет память, создает ссылку, инициализирует поля и вызывает конструктор.
Объект жив, пока на него есть хотя бы одна ссылка; когда ссылок не остается, он становится мусором.
Сборщик мусора (Garbage Collector, GC) автоматически удаляет ненужные объекты и освобождает память.
Перед удалением объекта JVM может (но не обязана) вызвать метод finalize(), поэтому полагаться на него нельзя.
Если ненужные объекты остаются достижимыми, GC не может их удалить, и возникает утечка памяти.
Даже жесткий диск, который в разы больше оперативной памяти, можно забить под завязку любимыми играми, сериалами и прочим. Чтобы этого не произошло, нужно следить за текущим состоянием памяти и удалять с компа ненужные файлы.
Какое отношение ко всему этому имеет программирование на Java? Прямое! Ведь при создании любого объекта Java-машиной под него выделяется память. В реальной большой программе создаются десятки и сотни тысяч объектов, под каждый из которых в памяти выделяется свой кусочек.Но как ты думаешь, сколько существуют все эти объекты? “Живут” ли они все время, пока работает наша программа? Разумеется, нет.
При всех достоинствах Java-объектов, они не бессмертны :) У объектов есть собственный жизненный цикл.
Сегодня мы чуть-чуть отдохнем от написания кода и рассмотрим этот процесс :) Тем более что он является очень важным для понимания работы программы и распоряжения ресурсами.
Кратко все этапы жизненного цикла объекта можно свести в таблицу:
Этап
Что происходит
Создание
по new JVM выделяет память, создает ссылку, инициализирует поля, вызывает конструктор
Жизнь
объект используется, пока на него есть хотя бы одна ссылка
Смерть (мусор)
ссылок не осталось (null или переприсвоение), объект становится мусором
Сборка
Garbage Collector удаляет объект и освобождает память (перед этим может вызвать finalize())
Как создается объект
Итак, с чего же начинается жизнь объекта? Как и у человека, с его рождения, то есть создания.
Cat cat = new Cat();//вот сейчас и начался жизненный цикл нашего объекта Cat!
При создании объекта происходит несколько шагов:
Виртуальная Java-машина выделяет необходимый объем памяти под объект.
Создается ссылка на объект (в нашем случае cat), чтобы иметь возможность его отслеживать.
Инициализируются все переменные (поля) объекта.
Вызывается конструктор, и вот наш свежий объект уже живет своей жизнью :)
Срок жизни у объектов разный, точных цифр здесь не существует. В любом случае, в течение какого-то времени он живет внутри программы и выполняет свои функции.
Когда объект умирает
Если говорить точно, объект является “живым” пока на него есть ссылки. Как только ссылок не остается, объект “умирает”.
Например:
public class Car {
String model;
public Car(String model) {
this.model = model;
}
public static void main(String[] args) {
Car lamborghini = new Car("Lamborghini Diablo");
lamborghini = null;
}
}
В методе main() объект машины Lamborghini Diablo перестает быть живым уже на второй строке. На него была всего одна ссылка, а теперь этой ссылке был присвоен null. Поскольку на Lamborghini Diablo не осталось ссылок, он становится “мусором”.
Ссылку при этом не обязательно обнулять:
public class Car {
String model;
public Car(String model) {
this.model = model;
}
public static void main(String[] args) {
Car lamborghini = new Car("Lamborghini Diablo");
Car lamborghiniGallardo = new Car("Lamborghini Gallardo");
lamborghini = lamborghiniGallardo;
}
}
Здесь мы создали второй объект, после чего взяли ссылку lamborghini и присвоили ей этот новый объект. Теперь на объект Lamborghini Gallardo указывает две ссылки, а на объект Lamborghini Diablo ни одной. Поэтому объект Diablo становится мусором.
Сборщик мусора (Garbage Collector)
И в этот момент в работу вступает встроенный механизм Java под названием сборщик мусора, или по-другому Garbage Collector, GC.
Сборщик мусора это внутренний механизм Java, который отвечает за освобождение памяти, то есть удаление из нее ненужных объектов.
Мы не зря выбрали для его изображения картинку с роботом-пылесосом. Ведь сборщик мусора работает примерно так же: в фоновом режиме он “ездит” по твоей программе, собирает мусор, и при этом ты с ним практически не взаимодействуешь.
Его работа это удалять объекты, которые уже не используются в программе. Таким образом он освобождает в компьютере память для других объектов. Помнишь в начале лекции мы говорили, что в обычной жизни тебе приходится следить за состоянием твоего компьютера и удалять старые файлы?
Так вот, в случае с Java-объектами сборщик мусора делает это вместо тебя.
Garbage Collector запускается многократно в течение работы твоей программы: его не надо вызывать специально и отдавать команды, хотя технически это возможно.
Позднее мы еще поговорим о нем и разберем процесс его работы более детально.
Метод finalize()
В момент, когда сборщик мусора добрался до объекта, перед самым его уничтожением, у объекта вызывается специальный метод, finalize().
Его можно использовать, чтобы освободить какие-то дополнительные ресурсы, которые использовал объект.
Метод finalize() принадлежит классу Object. То есть, наравне с equals(), hashCode() и toString(), с которыми ты уже познакомился ранее, он есть у любого объекта.
Его отличие от других методов в том, что он... как бы это сказать... весьма своенравен.
А именно: перед уничтожением объекта он вызывается далеко не всегда.
Программирование штука точная. Программист говорит компьютеру что-то сделать, компьютер это и делает. Ты, полагаю, уже привык к такому поведению, и тебе поначалу может быть сложно принять идею: “Перед уничтожением объектов вызывается метод finalize() класса Object. Или не вызывается. Как повезет!”
Тем не менее, это действительно так. Java-машина сама определяет, вызывать метод finalize() в каждом конкретном случае или нет.
Например, давай попробуем ради эксперимента запустить такой код:
public class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
public Cat() {
}
public static void main(String[] args) throws Throwable {
for (int i = 0 ; i < 1000000; i++) {
Cat cat = new Cat();
cat = null;//вот здесь первый объект становится доступен сборщику мусора
}
}
@Override
protected void finalize() throws Throwable {
System.out.println("Объект Cat уничтожен!");
}
}
Мы создаем объект Cat и уже в следующей строчке кода обнуляем единственную ссылку на него. И так миллион раз.
Мы явно переопределили метод finalize(), и он должен миллион раз вывести строку в консоль, каждый раз перед уничтожением объекта Cat.
Но нет! Если быть точным, на моем компьютере он отработал всего 37346 раз! То есть только в 1 случае из 27-ми установленная у меня Java-машина принимала решение вызвать метод finalize(), в остальных случаях сборка мусора проходила без этого.
Попробуй запустить этот код у себя: скорее всего, результат будет отличаться.
Как видишь, finalize() трудно назвать надежным партнером :)
Поэтому небольшой совет на будущее: не стоит полагаться на метод finalize() в случае с освобождением каких-то критически важных ресурсов. Может JVM его вызовет, а может нет. Кто знает? Если твой объект при жизни занимал какие-то суперважные для производительности ресурсы, например, держал открытым соединение с базой данных, лучше создай в своем классе специальный метод для их освобождения и вызови его явно, когда объект уже будет не нужен. Так ты точно будешь знать, что производительность твоей программы не пострадает.
Важно: начиная с Java 9 метод finalize() официально признан устаревшим (deprecated) именно из-за его ненадежности. В современном коде для освобождения ресурсов используют конструкцию try-with-resources или явные методы очистки, а не finalize().
Утечка памяти
В самом начале мы сказали, что работа с памятью и удаление мусора очень важны, и это действительно так. Неподобающая работа с ресурсами и непонимание процесса сборки ненужных объектов могут привести к утечке памяти. Это одна из самых известных ошибок в программировании.
Неправильно написанный программистом код может привести к тому, что для вновь созданных объектов каждый раз будет выделяться новая память, при этом старые, ненужные объекты будут недоступны для удаления сборщиком мусора.
Раз уж мы привели аналогию с роботом пылесосом, представь, что будет, если перед запуском робота разбросать по дому носки, разбить стеклянную вазу и оставить на полу разобранный конструктор Lego. Робот, конечно, попытается что-то сделать, но в один прекрасный момент он застрянет.
Для его правильной работы нужно держать пол в нормальном состоянии и убирать оттуда все, с чем не справится пылесос.
По такому же принципу работает и сборщик мусора. Если в программе будет оставаться много объектов, которые он не может собрать (как носок или Lego для робота-пылесоса), в один прекрасный момент память закончится. И зависнет не только написанная тобой программа, но и все остальные программы, запущенные в этот момент на компьютере. Для них тоже не будет хватать памяти.
Вот так выглядят в Java жизненный цикл объектов и сборщик мусора. Это не нужно заучивать: достаточно просто понять принцип работы. В следующей лекции мы поговорим об этих процессах подробнее, а пока можешь вернуться к решению задач JavaRush :) Успехов!
Подробнее о методе можно почитать в документации Oracle (класс Object).
Это путь объекта от создания до удаления: он создается оператором new, живет и используется, пока на него есть ссылки, а когда ссылок не остается, становится мусором и удаляется сборщиком мусора.
Что происходит при создании объекта?
JVM выделяет память под объект, создает на него ссылку, инициализирует поля значениями и вызывает конструктор. После этого объект готов к работе.
Когда объект становится мусором?
Когда на него не остается ни одной ссылки: например, ссылке присвоили null или перенаправили ее на другой объект. Такой объект недостижим и подлежит удалению.
Что такое сборщик мусора (Garbage Collector)?
Это встроенный механизм Java, который в фоновом режиме находит объекты без ссылок и удаляет их, освобождая память. Вызывать его вручную не нужно.
Можно ли полагаться на метод finalize()?
Нет. JVM вызывает finalize() перед удалением объекта не всегда, а начиная с Java 9 метод считается устаревшим. Ресурсы лучше освобождать явно или через try-with-resources.
Что такое утечка памяти?
Это ситуация, когда ненужные объекты остаются достижимыми по ссылкам, поэтому сборщик мусора не может их удалить. Память постепенно заканчивается, и программа может зависнуть.
Senior Java-разработчик и преподаватель в LearningTree
Senior Java-разработчик и преподаватель Java на курсах программирования Learning Tree International. Иногда пишу всякие глупости п ... [Читать полную биографию]
Метод finalize():
Вызывался перед сборкой мусора (GC),
Устарел начиная с Java 9, и помечен как @Deprecated(forRemoval = true) с Java 18+,
Вместо него используют AutoCloseable или try-with-resources.
про @Override пропустил или не видел, что это и для чего... в заданиях тоже в подсказках он был, но я проходил задания, не используя его... так вот, нужно его использовать и в каких моментах, если нужно?
Если в двух словах то это специальная метка, которой помечается метод который мы переопределяем. В данном примере меткой помечен метод finalize(), который есть в классе Object. Так как класс Object это класс родитель для всех классов в Java , то этод метод есть в любом создаваемом классе, мы просто пишем свою реализацию метода. А метку ( или правильнее аннотацию) указываем просто для того что бы компилятор проверил что мы не допустили ошибки, и переопределяем метод именно класа родителя. Если мы напишем метод
Override
public void finalize(String name){
something code......
}
то компилятор не скомпилирует программу, потому, что такого медота с параметром String в класе родителе нет.
Начиная с Java 9, метод finalize() помечен как устаревший (deprecated), поскольку его использование может вызывать проблемы с производительностью и управлением ресурсами.
Вместо него рекомендуется использовать такие механизмы, как try-with-resources (для автозакрытия ресурсов) или явное управление жизненным циклом объекта.
Ваш пример показывает принцип работы сборщика мусора, но не всегда вызовет вывод сообщения. Для обучения это полезный код, но в реальных проектах лучше использовать современные методы управления памятью и ресурсами.
вот вопрос "" То есть только в 1 случае из 27-ми установленная у меня Java-машина принимала решение вызвать метод finalize() — в остальных случаях сборка мусора проходила без этого. ""
Это ведь значит что все объекты удалены. Просто метод finalize не вызван. Так для чего нам руками удалять?
Речь шла не об удалении объектов - объекты сборщик мусора сам удалит. Насколько понимаю, автор пишет о том, что если объект задействовал какой-то ценный или критический ресурс, то для надёжности нужно позаботиться об освобождении этого ресурса, перед тем как зачистить ненужный уже объект.
Например, объекту нужно прочитать данные из таблицы базы данных. Он заблокировал эту таблицу, чтобы какой-то другой объект не вносил туда изменения, пока он читает. Потом выполнил свою работу и "умер". GC убрал мусор. А таблица в базе данных осталась заблокированной. Тоже самое открытые файлы или порты для передачи данных.
"Если говорить точно, объект является “живым” пока на него есть ссылки."
Для автора статьи такое утверждение - серьёзная ошибка.
Следует понимать, что объект является "живым" до тех пор, пока он является достижимым.
Наличие ссылок на объект не является признаком того, что он "жив".
А у меня всего 14868 😵. Но можно ли сказать что все остальные живы ? Если не используется метод - ( finalize() ), разве можно сказать что объект остался жив ?
Кто пробовал запустить у себя код как в примере из лекции ( System.out.println("Объект Cat уничтожен!");) Я неожиданно озадачился как можно посчитать количество строк или же вызовов в консоли. Кто знает?
P.S. Пробовал создать счётчик, но дилемма именно в том, что finalize() вызывается не всегда
Я в общем просто скопировал вывод, открыл обычный файл txt, но с помощью среды разработки VS code (наверное и IntelliJ тоже можно), а потом вставил вывод туда, а как мы знаем у среды разработки слева номера есть, так и смотрел
как вариант, можно объявить в классе статическое поле равное 0, и в методе finalize делать инкремент этого поля, после цикла в main вывести значение в консоль
Все пишут, что finalize() устарел. Как мне правильно реагировать на эту информацию?) Garbage collector всё также работает в автоматическом режиме и это изменение никак не повлияло на процесс разработки, или я пропущу что-то важное, если не копну глубже эту тему?
In Java 9, the method java.lang.Object.finalize() is deprecated, and it's not recommended to use it for cleaning up resources. Instead, it's recommended to use try-with-resources statement or the java.lang.AutoCloseable interface to release resources when they are no longer needed. The garbage collector in Java will handle removing the objects from memory automatically when they are no longer reachable.
class MyResource implements AutoCloseable {
public void doSomething() {
// Do something with the resource
}
@Override
public void close() throws Exception {
// Release the resource
}
}
public class Main {
public static void main(String[] args) {
try (MyResource resource = new MyResource()) {
resource.doSomething();
} catch (Exception e) {
// Handle exception
}
}
}
In this example, the resource is automatically closed when the try block is exited, whether it is because of a return statement, an exception, or simply reaching the end of the block.
Похоже данная статья уже не актуальна, разве что как историческая справка, так как начиная с Java 9 метод finalize -> Depricated.
А в томе 1 11-того издания Кея Хорстманна, написано:
"ВНИМАНИЕ! Ни в коем случае не пользуйтесь методом finalize () для освобождения ненужных
объектов из оперативной памяти. Этот метод предназначался раньше для вызова непосредственно
перед удалением объекта сборщиком "мусора". Но дело в том, что вы не можете знать заранее, когда
именно следует вызывать данный метод. Кроме того, он больше не рекомендован к применению
в прикладных программах на Java."
Скомпилировав код на JDK 8 (1.8) код приведенный в статье на первом миллионе вообще ни разу не сработал finalize(), только начиная со второго finalize() иногда отрабатывал.
Поддерживаю, метод finalize() уже морально устарел.
Из JavaDocs о методе finalize():"Устарел! (c Java 9)
Механизм финализации по своей сути проблематичен. Завершение может привести к проблемам с производительностью, взаимоблокировкам и зависаниям. Ошибки в финализаторах могут привести к утечке ресурсов; нет возможности отменить финализацию, если в ней больше нет необходимости; и нет порядка среди вызовов финализации методов разных объектов. Кроме того, нет никаких гарантий относительно тайминга финализации. Метод finalize() может быть вызван для финализируемого объекта только после неопределенной задержки, если вообще будет вызван. Классы, экземпляры которых содержат ресурсы не в куче (non-heap), должны предоставлять метод для включения явного освобождения этих ресурсов, а также должны реализовывать AutoCloseable, если это необходимо. Cleaner и PhantomReference предоставляют более гибкие и эффективные способы высвобождения ресурсов, когда объект становится недоступным."
Напомню, Java 9 вышла в конце 2017 года!
Чтобы увидеть сколько раз отработал метод finalize(), можно создать статическую переменную и использовать ее в этом методе:
public class Finalize {
private static int counter = 0;
public static void main() {
for (int i = 0; i < 1000000; i++) {
Finalize f = new Finalize();
System.out.println("Объект " + i);
f = null;
}
System.out.println(counter);
}
@Override
public void finalize() {
counter++;
}
}
Мы не знаем какие решения примет JVM в разных отрезков программы, поэтому не можем с уверенностью сказать что объект точно будет удален. Мы можем сказать что объект пригоден для удаления.
Очень понравилась аналогия с пылесосом и хаосом на полу из носок, битого стекла и конструктора... Сразу вспомнил Валли и убиральщика, который впадал в диссонанс от грязи земного пришельца))).
Метод finalize() принадлежит классу Object. То есть, наравне с equals(),
hashCode() и toString(), с которыми ты уже познакомился ранее, он есть у
любого объекта.
Из этих строк я понимаю, что метод finalize() по идее должен был вызываться каждый раз при удалении объекта. Но этого не происходит по каким-то причинам или вернее, это происходит не всегда.
Сделано это было с целью наглядно показать, что JVM не всегда вызывает метод finalize(). Без System.out.println это происходит скрытно, без какой-либо визуализации, а на память и работу программы может сильно повлиять. Таким образом, статья призывает нас к тому, чтобы принудительно вызывать в своем классе специальный метод для освобождения памяти, при удалении объектов, затрачивающих большие ресурсы памяти.
String name1 = "Masha Frolova";
String name2 = "Маша";
if (name1.equals(name2) == true) {
System.out.println("Вроде бы Маша - это я :) ");
}else{
System.out.println("Маша, но это не я ))");
}
Можно вопрос ? из текста статьи "Его можно использовать, чтобы освободить какие-то дополнительные ресурсы, которые использовал объект." - а если jvm не вызывает его, то память занятая объектами освобождается не вся и возникает утечка памяти?
Вроде бы разобрался с методом finalize(). Весьма болезненная штука, но работать, при необходимости, может, хотя и не всегда...
Результаты тестов: Если припрёт (например, при нехватке памяти) JVM может финализировать все объекты, ожидающие своей очереди. Но, если работа JVM завершится раньше, то многие объекты финализации не дождутся...
А мне не понятно зачем метод @Override (переопределять). для чего он нужен и как он работает. в интернете нормально не чего не пишут. и понять не могу. так как в одном задании он есть. а что с ним делать не понятно.
ps: Прошу мне прислать сообщением ваш комент. а то не разберусь.
Эта аннотация нужна для того, чтобы убедиться, что объявленный метод действительно переопределяет соответствующий метод класса-предка, а не является новым или перегруженным. Т.е. если Вы объявляете метод с аннотацией @Override, то метод с точно такой же сигнатурой (имя метода + типы параметров), должен быть объявлен в одном из классов-предков, иначе возникнет ошибка компиляции.
Подробнее об этой аннотации можно прочитать здесь и здесь.
уже int закончился, а вывода все равно нет.
for (int i = 0; i < 1000000000; i++) {
Car car = new Car("Lamba", 2012);
car=null;
}
}
@Override
protected void finalize() throws Throwable {
System.out.println("Объект Car уничтожен!");
}
}
Ещё немного изменений кода для большей наглядности.
В классе:
public class Solution {
static int counter = 0; //создаём счётчик созданных объектов класса Solution
static int garbage = 0; //создаём счётчик запуска переопределённого метода finalize()
String name;
public Solution(int counter) throws IOException {
this.counter = counter;
System.out.println(counter); //выводим в консоль номер созданного объекта
}
В цикле метода main:
public static void main(String[] args) throws Throwable {
for (int i = 1 ; i <= 100000; i++) {
Solution solution = new Solution(i);
solution.name = "№" + i; //присваиваем имя созданному объекту
solution = null; //вот здесь первый объект становится доступен сборщику мусора
}
}
И в переопределённом методе finalize():
@Override
protected void finalize() {
garbage++;
System.out.println("Объект " + this.name + " уничтожен! " + garbage); //выводим в консоль информацию об удалённом объекте и счётчик вызова метода
}
И тогда, на выходе в консоли мы видим порядковый номер счётчика только что созданных объектов (при отработке конструктора класса), и текст сработавшего метода finalize(), который содержит имя объекта попавшего под "замес" и порядковый номер счётчика запусков "пылесоса".
Пример вывода из моей консоли (который вообще никакой логике не поддаётся):
...
Объект №11744 уничтожен! 95
94164
Объект №69564 уничтожен! 96
94165
94166
94167
Объект №70059 уничтожен! 97
94168
Объект №6928 уничтожен! 98
94169
Объект №7252 уничтожен! 99
94170
Объект №7410 уничтожен! 100
94171
Объект №7689 уничтожен! 101
94172
Объект №23100 уничтожен! 102
94173
Объект №23389 уничтожен! 103
94174
Объект №23661 уничтожен! 104
...
public static void main(String[] args) throws Throwable {
for (int i = 0 ; i < 1000000; i++) {
Cat cat = new Cat();
cat.name="Name"+i;
cat = null;//вот здесь первый объект становится доступен сборщику мусора
}
}
@Override
protected void finalize() throws Throwable {
System.out.println("Объект "+this.name+" уничтожен!");
}
И после выполнения видно, как объекты уничтожаются не попорядку, а по каким-то другим условиям.
Отрывок вывода на консоль:
.............................................
Объект Name737071 уничтожен!
Объект Name737070 уничтожен!
Объект Name737069 уничтожен!
Объект Name737068 уничтожен!
Объект Name737067 уничтожен!
.............................................
Не понимаю! Почему когда в коде, в цикле вместо миллиона пишу 204341 и меньше - в консоль вообще !!! ни разу не выводится фраза "Объект Cat уничтожен!"
https://skr.sh/s5WP7uDoUVi
Интересный вопрос!
Могу лишь предположить, что стандартный уборщик мусора работает в отдельном потоке ( на который мы не влияем) и удаляет ссылку раньше чем до него добирается переопределенный метод!
Вот этот-
@Override
protected void finalize() throws Throwable {
System.out.println("Объект Cat уничтожен!");
}
P/S
Есть один вариант! но он тебе не понравится! :)
подсчитать число выведенных строк - "Объект Cat уничтожен!"
Их должно быть меньше чем 1 000 000.
А при переопределении метода finalize() его изначальные функции разве не стираются? Не совсем понятно, если мы переопределили метод, добавив только sout, выполняет ли он свою первоначальную функцию и, если нет, почему он по-прежнему вызывается только в ряде случаев?
Более красиво было бы так:
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Объект Cat уничтожен!");
}
На месте авторов статьи, я бы подредактировал
Автор его только переопределил, а вот алгоритм вызова заложен в GC так что в данном случае он просто показал на какой итерации сработал вызов метода перед удалением.
вот как выглядит в приложении лекция. Все ссылки на код окрашены белым и не понятно о чем речь. Приложение - очень удобная штука, чтобы учиться в дороге. Надеюсь скоро исправят.
ткните плиз где именно ОБЪЕКТ диабло , насколько я понял объект у нас Car, а lamborghini - это экземпляр объекта lamborghini, и в переменную lamborghini занесено значение "lamborghini daiblo" поправьте если не прав
@Override - так называемая аннотация переопределения. Она следит за тем, чтобы при переопределении метода базового (родительского) класса из-за опечатки в названии метода случайно не создать какой-то новый метод, тем самым оставив нужный метод непереопределённым. Такой баг потом пришлось бы долго выискивать, а @Override сразу сообщит о нём подчёркиванием и не позволит ошибиться.
Показывает, что метод переопределен. Переопределять можно и без него, но аннотация позволяет избежать ошибок, которые выше описали. Если не знаешь про переопределение, то потом узнаешь :)
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
finalize(). Весьма болезненная штука, но работать, при необходимости, может, хотя и не всегда... Результаты тестов: Если припрёт (например, при нехватке памяти) JVM может финализировать все объекты, ожидающие своей очереди. Но, если работа JVM завершится раньше, то многие объекты финализации не дождутся...finalize(), или аннотацию@Override?@Override, то метод с точно такой же сигнатурой (имя метода + типы параметров), должен быть объявлен в одном из классов-предков, иначе возникнет ошибка компиляции. Подробнее об этой аннотации можно прочитать здесь и здесь.