В 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)); //Ошибка компиляции
}
А теперь давайте устроим небольшое собеседование. Ведь основана цель прохождения курса JavaRush – это стать Java разработчиком и устроиться на интересную и хорошо оплачиваемую работу.
И так, начнем.
Что можно сказать про массив, когда он объявлен
final
?Известно, что класс
String
—immutable
, класс объявленfinal
, значение строки хранится в массивеchar
, который отмечен ключевым словомfinal
.
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
Можно ли подменить значение у объекта String
(не меняя ссылки на объект)?
Это реальные вопросы с собеседования. И как показывает практика, многие отвечаю на них не правильно. Понимание использования ключевого слова final
, особенно для ссылочных переменных — очень важно.
Пока вы размышляете, небольшое отступление к команде JavaRush. Просьба добавить в текстовом редакторе некий блок, позволяющий скрывать содержимое, а при нажатии по нему — показывать это содержимое.
Ответы:
Т.к. массив – это объект, то
final
означает, что после присвоения ссылки на объект, уже нельзя ее изменить, но можно изменять состояние объекта.final int[] array = {1,2,3,4,5}; array[0] = 9; //ок, т.к. изменяем содержимое массива – {9,2,3,4,5} array = new int[5]; //ошибка компиляции
Да, можно. Ключевой момент – это понимание использования колючего слова
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.
Всем удачи!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ