Модификатор final

Где может использоваться модификатор final?Что означает
В объявлении статического поля класса

В объявлении нестатического поля класса
Для переменных примитивного типа это означает, что однажды присвоенное значение не может быть изменено.

При создании экземпляра значение должно быть присвоено в объявлении класса или конструкторе

Для ссылочных переменных это означает, что после присвоения объекта, нельзя изменить ссылку на данный объект. Это важно! Ссылку изменить нельзя, но состояние объекта изменять можно.
В объявлении методаДля метода final означает, что он не может быть переопределен в подклассах. Это полезно, когда мы хотим, чтобы исходную реализацию нельзя было переопределить.
В объявлении классаДля класса это означает, что класс не сможет иметь подклассов, т.е. запрещено наследование

Это полезно при создании immutable (неизменяемых) объектов, например, класс String объявлен, как final
В объявлении параметра метода
effectively finalС java 8 — стримах (Stream API).
https://javarush.ru/groups/posts/591-vot-tak-final

Final класс

Следует также отметить, что к абстрактным классам (с ключевым словом abstract), нельзя применить модификатор final, т.к. это взаимоисключающие понятия.

public final class String{
 // ...
}

Final метод

public class SuperClass{
    public final void printReport(){
        System.out.println("Report");
    }
}

effectively final

С java 8 появилось понятие — effectively final. Применяется оно только к переменным (в том числе аргументам методов). Суть в том, что не смотря на явное отсутствие ключевого слова final, значение переменной не изменяется после инициализации. Другими словами, к такой переменной можно подставить слово final без ошибки компиляции. effectively final переменные могут быть использованы внутри локальных классов (Local Inner Classes), анонимных классов (Anonymous Inner Classes), стримах (Stream API).

public void someMethod(){
    // В примере ниже и a и b - effectively final, тк значения устанавливаютcя однажды:
    int a = 1;
    int b;
    if (a == 2) b = 3;
    else b = 4;
    // с НЕ является effectively final, т.к. значение изменяется
    int c = 10;
    c++;

    Stream.of(1, 2).forEach(s-> System.out.println(s + a)); //Ок
    Stream.of(1, 2).forEach(s-> System.out.println(s + c)); //Ошибка компиляции
}

Блиц

1. Что можно сказать про массив, когда он объявленfinal?

Т.к. массив – это объект, то final означает, что после присвоения ссылки на объект, уже нельзя ее изменить, но можно изменять состояние объекта.
final int[] array = {1,2,3,4,5};
array[0] = 9;	//ок, т.к. изменяем содержимое массива – {9,2,3,4,5}
array = new int[5]; //ошибка компиляции

2. Можно ли подменить значение у объекта String (не меняя ссылки на объект)?

Известно, что класс String — immutable, класс объявлен final, значение строки хранится в массиве char, который отмечен ключевым словом final. Да, можно. Ключевой момент – это понимание использования колючего слова final с объектами. Для подмены значения использует ReflectionAPI.
import java.lang.reflect.Field;

class B {
    public static void main(String[] args) throws Exception {
        String value = "Old value";
        System.out.println(value);

        //Получаем поле value в классе String
        Field field = value.getClass().getDeclaredField("value");
        //Разрешаем изменять его
        field.setAccessible(true);
        //Устанавливаем новое значение
        field.set(value, "JavaRush".toCharArray());

        System.out.println(value);

        /* Вывод:
         * Old value
         * JavaRush
         */
    }
}

Обратите внимание, что если бы мы попытались изменить подобным образом финальную переменную примитивного типа, то ничего бы не вышло. Предлагаю вам самостоятельно в этом убедить: создать Java класс, например, с final int полем и попробовать изменить его значение через Reflection API.

Оцените автора
Kosenkov.Pro
Добавить комментарий