Где может использоваться модификатор final? | Что означает |
---|---|
В объявлении статического поля класса В объявлении нестатического поля класса | Для переменных примитивного типа это означает, что однажды присвоенное значение не может быть изменено. При создании экземпляра значение должно быть присвоено в объявлении класса или конструкторе Для ссылочных переменных это означает, что после присвоения объекта, нельзя изменить ссылку на данный объект. Это важно! Ссылку изменить нельзя, но состояние объекта изменять можно. |
В объявлении метода | Для метода final означает, что он не может быть переопределен в подклассах. Это полезно, когда мы хотим, чтобы исходную реализацию нельзя было переопределить. |
В объявлении класса | Для класса это означает, что класс не сможет иметь подклассов, т.е. запрещено наследование Это полезно при создании immutable (неизменяемых) объектов, например, класс String объявлен, как final |
В объявлении параметра метода | |
effectively final | С java 8 — стримах (Stream API). |
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.