Рубрики
Без рубрики

Различия между Final, Finally и Finalize в Java

Узнайте разницу между ключевыми словами Final, Finally и Finalize в Java

Автор оригинала: baeldung.

1. Обзор

В этом уроке мы рассмотрим три ключевых слова Java: final, finally и finalize.

Хотя эти ключевые слова похожи друг на друга, каждое из них имеет совершенно другое значение в Java. Мы изучим назначение каждого из них и рассмотрим некоторые примеры с помощью небольшого кода.

2. последнее ключевое слово

Давайте сначала взглянем на ключевое слово final , где его использовать и почему. Мы можем применить ключевое слово final к объявлениям классов, методов, полей, переменных и параметров методов.

Однако это не оказывает одинакового эффекта на каждого из них :

  • Создание класса final означает, что расширить этот класс будет невозможно
  • Добавление final в метод означает, что переопределить этот метод будет невозможно
  • Наконец, размещение final перед полем, переменной или параметром означает, что после присвоения ссылки она не может быть изменена (однако, если ссылка относится к изменяемому объекту, ее внутреннее состояние может измениться, несмотря на то, что оно окончательное).

Подробную статью о ключевом слове final можно найти здесь .

Давайте посмотрим, как работает ключевое слово final на некоторых примерах.

2.1. конечные поля, Параметры и Переменные

Давайте создадим Родительский класс с двумя int полями, окончательным одним и обычным не окончательным:

public class Parent {

    int field1 = 1;
    final int field2 = 2;

    Parent() {
        field1 = 2; // OK
        field2 = 3; // Compilation error
    }

}

Как мы видим, компилятор запрещает нам присваивать новое значение полю 2 .

Давайте теперь добавим метод с обычным и последним аргументом:

    void method1(int arg1, final int arg2) {
        arg1 = 2; // OK
        arg2 = 3; // Compilation error
    }

Аналогично полям, невозможно назначить что-либо arg2 , поскольку оно объявлено окончательным.

Теперь мы можем добавить второй метод, чтобы проиллюстрировать, как это работает с локальными переменными:

    void method2() {
        final int localVar = 2; // OK
        localVar = 3; // Compilation error
    }

Ничего удивительного не происходит, компилятор не позволяет нам присваивать новое значение local Var после его первого присвоения.

2.2. окончательный метод

Теперь предположим , что мы делаем метод2 окончательным и создаем подкласс Родительского , скажем Дочернего , в котором мы пытаемся переопределить оба его метода суперкласса:

public class Child extends Parent {

    @Override
    void method1(int arg1, int arg2) {
        // OK
    }
    
    @Override
    final void method2() {
        // Compilation error
    }

}

Как мы видим, нет никаких проблем с переопределением метода 1() , но мы получаем ошибку компиляции при попытке переопределить метод 2() .

2.3. заключительный урок

И, наконец, давайте сделаем Дочерний класс окончательным и попробуем создать его подкласс, Внук :

public final class Child extends Parent { 
    // ... 
}
public class GrandChild extends Child {
    // Compilation error
}

И снова компилятор жалуется. Класс Дочерний является окончательным, и поэтому его невозможно расширить.

3. наконец, Заблокируйте

Блок наконец является необязательным блоком для использования с оператором try/catch . В этом блоке мы включаем код для выполнения после структуры try/catch , независимо от того, возникает исключение или нет .

Его даже можно использовать с блоком try без какого-либо блока catch при условии, что мы включим наконец блок. Затем код будет выполнен после попытки или после возникновения исключения.

Здесь у нас есть подробная статья об обработке исключений в Java .

Теперь давайте продемонстрируем наконец блок в коротком примере. Мы создадим фиктивный метод main() со структурой try/catch/finally :

public static void main(String args[]) {
    try {
        System.out.println("Execute try block");
        throw new Exception();
    } catch (Exception e) {
        System.out.println("Execute catch block");
    } finally {
        System.out.println("Execute finally block");
    }
}

Если мы запустим этот код, он выведет следующее:

Execute try block
Execute catch block
Execute finally block

Давайте теперь изменим метод, удалив блок catch (и добавив выбрасывает исключение в подпись).:

public static void main(String args[]) throws Exception {
    try {
        System.out.println("Execute try block");
        throw new Exception();
    } finally {
        System.out.println("Execute finally block");
    }
}

Результат сейчас:

Execute try block
Execute finally block

Если мы теперь удалим инструкцию throw new Exception () , мы сможем заметить, что результат останется прежним. Наше наконец выполнение блока происходит каждый раз.

4. доработать Метод

И, наконец, метод finalize является защищенным методом, определенным в классе объектов . Он вызывается сборщиком мусора для объектов, на которые больше нет ссылок и которые были выбраны для сбора мусора .

Как и любой другой не окончательный метод, мы можем переопределить этот метод, чтобы определить поведение, которое должен иметь объект при сборе сборщиком мусора .

Опять же, подробную статью, описывающую метод finalize , можно найти здесь .

Давайте посмотрим на пример того, как это работает. Мы будем использовать System.gc() , чтобы предложить JVM для запуска сборки мусора :

    @Override
    protected void finalize() throws Throwable {
        System.out.println("Execute finalize method");
        super.finalize();
    }
    public static void main(String[] args) throws Exception {
        FinalizeObject object = new FinalizeObject();
        object = null;
        System.gc();
        Thread.sleep(1000);
    }

В этом примере мы переопределяем метод finalize() в нашем объекте и создаем метод main () , который создает экземпляр нашего объекта и немедленно удаляет ссылку, установив для созданной переменной значение null .

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

Результат выполнения этого кода должен быть:

Execute finalize method

Обратите внимание, что переопределение метода finalize() считается плохой практикой, поскольку его выполнение зависит от сборки мусора , которая находится в руках JVM . Кроме того, этот метод устарел начиная с Java 9.

5. Заключение

В этой статье мы кратко обсудили различия между тремя похожими ключевыми словами Java: final, finally и finalize .

Полный код статьи можно найти на GitHub.