Где может использоваться модификатор final
Перейти к содержимому

Где может использоваться модификатор final

  • автор:

Ключевое слово final

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

1. final переменные

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

final переменная класса, объявленная как не static , должна инициализироваться при объявлении или в теле конструктора или блоке инициализации, иначе произойдет ошибка компиляции.

Кроме переменных, объявленными как final могут быть параметры метода и локальные переменные. final переменные, объявленные как static , должны быть проинициализированы при объявлении или в блоке инициализации, также объявленном как static . В противном случае опять получится ошибка компиляции.

Следующий пример показывает различные варианты объявления завершенных переменных:

public class FinalVariables < public static final int FILE_NEW = 1; private final String someString = "something"; public static void print(final double d) < // FILE_NEW = 2; final String str; str = "someString"; // str = ""; // d = 4; System.out.println("FILE_NEW = " + FILE_NEW); System.out.println("str = " + str); System.out.println("d header2">2. Константы Java 
Константы – это переменные, значение которых не меняется. Константами в Java принято называть public static final переменные класса. Имена констант следует задавать только заглавными буквами, а слова в имени разделять знаком подчеркивания: MAX_WEIGHT.

Константы часто используются для борьбы с магическими (или волшебными) числами, то есть непонятно что означающими числами или строками. Например, следующий код содержит несколько раз повторяющееся магическое число 9.81:

public class PhysicsMagicNumber < public static double potentialEnergy(double mass, double height) < return mass * height * 9.81; >public static double getVelocity(double time) < return time * 9.81; >public static double getDistance(double time) < return 9.81 * time * time / 2; >>

Давайте перепишем код введя константу с именем ACCELERATION . Какие преимущества дает нам введение константы? Во-первых, имя константы уже объясняет значение этого числа, и, во-вторых, при желании изменить значение ACCELERATION , это можно сделать в одном месте. После рефакторинга:

public class Physics < public static final double ACCELERATION = 9.81; public static double potentialEnergy(double mass, double height) < return mass * height * ACCELERATION; >public static double getVelocity(double time) < return time * ACCELERATION; >public static double getDistance(double time) < return ACCELERATION * time * time / 2; >>

3. final методы

Чтобы запретить переопределение метода в классах наследниках, в начале его объявления следует указать ключевое слово final . Такие методы еще называют завершенными.

Нет смысла объявлять метод private final так как private метод не виден в наследниках, соответственно не может быть переопределен.

Также конструктор не может быть объявлен как final .

Класс O содержит завершенный метод, который не может быть переопределен в классе наследнике P . При попытке возникнет ошибка компиляции:

public class O < final void method() < System.out.println("Это завершенный метод"); >>
public class P extends O < //Этот метод не может быть переопределен /* void method() < System.out.println("Недопустимо"); >*/ >

4. final классы

Для предотвращения наследование класса в начале объявления класса следует указать ключевое слово final . Объявление класса завершенным неявно делает завершенными и все его методы. Одновременное объявление класса, как abstract и final недопустимо.

final class А < //… >class B extends A 
  • Процедурное и объектно-ориентированное программирование
  • Принципы ООП
  • Классы и объекты
  • Конструктор
  • Ключевое слово this
  • Перегрузка
  • Стек и куча
  • Передача объектов в методы
  • Java varargs
  • Рекурсия
  • Сборщик мусора и метод finalize
  • Наследование
  • Ключевое слово super
  • Модификаторы доступа
  • Геттеры и сеттеры
  • Переопределение методов
  • Абстрактные классы и методы
  • Задания

Вот так final…

Java-университет

Вот так final… - 1

В java есть ключевое слово – final . Оно может применяться к классам, методам, переменным (в том числе аргументам методов). Для класса это означает, что класс не сможет иметь подклассов, т.е. запрещено наследование. Это полезно при создании immutable (неизменяемых) объектов, например, класс String объявлен, как final .

 public final class String < >class SubString extends String < //Ошибка компиляции >

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

 public class SuperClass < public final void printReport()< System.out.println("Report"); >> class SubClass extends SuperClass < public void printReport()< //Ошибка компиляции System.out.println("MyReport"); >> 

Для переменных примитивного типа это означает, что однажды присвоенное значение не может быть изменено. Для ссылочных переменных это означает, что после присвоения объекта, нельзя изменить ссылку на данный объект. Это важно! Ссылку изменить нельзя, но состояние объекта изменять можно. С 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 ?
  2. Известно, что класс String — immutable , класс объявлен final , значение строки хранится в массиве char , который отмечен ключевым словом final .
 public final class String implements java.io.Serializable, Comparable, CharSequence < /** The value is used for character storage. */ private final char value[]; 
  1. Т.к. массив – это объект, то final означает, что после присвоения ссылки на объект, уже нельзя ее изменить, но можно изменять состояние объекта.
 final int[] array = ; array[0] = 9; //ок, т.к. изменяем содержимое массива – array = new int[5]; //ошибка компиляции 
 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. Всем удачи!

Что означает модификатор final в полях классов?

Я заметил, что профессиональные разработчики нередко объявляют поля классов в джаве как final, например:

@Component public class LinkResolver implements GraphQLResolver  < private final UserRepository userRepository; public LinkResolver(UserRepository userRepository) < this.userRepository = userRepository; >public User postedBy(Link link) < if (link.getUserId() == null) < return null; >return userRepository.findById(link.getUserId()); > > 

Для чего это делается? Я видел, как в проекте final объявляются не только поля сервисов и репозиториев, но и поля дата-классов. Расскажите об этом больше, пожалуйста.

Отслеживать
18.5k 4 4 золотых знака 31 31 серебряный знак 45 45 бронзовых знаков
задан 5 мар 2019 в 9:57
2,513 3 3 золотых знака 31 31 серебряный знак 50 50 бронзовых знаков
Чтобы можно было только один раз инициализировать переменную, например только в конструкторе
5 мар 2019 в 10:00

4 ответа 4

Сортировка: Сброс на вариант по умолчанию

  1. Удобно чисто визуально. Если видишь, что переменная final , то точно знаешь, что она не будет меняться.
  2. Без final никак, если ты локальную переменную собираешься использовать в анонимных классах/замыканиях.
  3. final - это подсказка компилятору. Если он видит финальные поля, то может произвести определённые оптимизации кода.

Отслеживать
ответ дан 5 мар 2019 в 10:03
Suvitruf - Andrei Apanasik Suvitruf - Andrei Apanasik
32.2k 15 15 золотых знаков 61 61 серебряный знак 93 93 бронзовых знака
А final, примененное к полям, может иметь какое-то значение при наследовании?
5 мар 2019 в 10:29
@typemoon принципиальной нет. Поля всё также остаются неизменяемыми.
5 мар 2019 в 10:31
в ответе кроется одно коварство, некоторые новички путают final с иммутабельностью.
5 мар 2019 в 10:45
4. final-поля безопасно публикуются в многопоточном окружении
5 мар 2019 в 10:46
@StrangerintheQ ещё про рефлексию можно написать )
5 мар 2019 в 11:20

Многа букав: По-умолчанию ставятся максимальные ограничения. Снимать ограничение (final) с поля имеет смысл только если на это есть причина (поле изменяется).

С помощью final отмечаются поля, которые инициализируются только один раз. Отмечать такое поле как final технически необязательно, но если этого не сделать, то:

  • останется возможность ошибки: разработчик опечатается и переприсвоит значение;
  • при чтении кода возникнут вопросы: «Где это поле изменяется? И как это повлияет на остальной код?». Модификатор final говорит разработчику что лишние сценарии рассматривать не требуется.

Вообще значительная часть программирования это расстановка ограничений. Чем меньше операций можно выполнить с кодом — тем легче о нем рассуждать — тем проще проверить его правильность. Поэтому разработчики стараются устанавливать для полей/переменных максимально жесткие ограничения, чтобы снизить количество возможных сценариев их использования.

По аналогичной причине поля, которые не используются вне класса, объявляются как private . Поле с доступом по-умолчанию оставит лишние сценарии использования и, вместе с ними, простор для ошибок и вопросов («Из какого класса к нему идет обращение и зачем?»).

Многие IDE и средства анализа кода (например, PMD) отслеживают поля и переменные, которые могут быть отмечены как final и выдают соответствующие предупреждения.

На практике поля, как важную часть классов, везде где возможно отмечают как final. На локальных переменных и аргументах методов же обычно «экономят», т.к. срок жизни у них короче и риск побочных эффектов меньше. Для них наоборот используют final только там где это явно необходимо (использование в анонимных классах). Например, в Вашем коде аргументы конструктора ( userRepository ) и метода ( link ) могли бы быть отмечены как final , но не отмечены для простоты кода.

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

Модификатор final - это способ, с помощью которого вы можете контролировать работу своей программы и ее составных частей. Это один из Ваших инструментов:

final_2

Суть модификатора final - сделать дальнейшее изменение объекта невозможным. С английского "final" можно перевести как "последний, окончательный":

Вы можете применять этот модификатор тремя способами: для класса, для поля (переменной) и для метода.

final_3
Final для полей

Если вы хотите, чтобы после инициализации никто не мог бы изменить вашу переменную, напишите слово "final":

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *