В Java мы используем final
ключевое слово с переменными, чтобы указать, что его значения не должны изменяться. Но я вижу, что вы можете изменить значение в конструкторе / методах класса. Опять же, если переменная - static
это ошибка компиляции.
Вот код:
import java.util.ArrayList;
import java.util.List;
class Test {
private final List foo;
public Test()
{
foo = new ArrayList();
foo.add("foo"); // Modification-1
}
public static void main(String[] args)
{
Test t = new Test();
t.foo.add("bar"); // Modification-2
System.out.println("print - " + t.foo);
}
}
Выше код работает нормально и без ошибок.
Теперь измените переменную как static
:
private static final List foo;
Теперь это ошибка компиляции. Как это final
действительно работает?
Ответы:
Вы всегда может инициализировать в
final
переменном. Компилятор гарантирует, что вы можете сделать это только один раз.Обратите внимание, что вызов методов для объекта, хранящегося в
final
переменной, не имеет ничего общего с семантикойfinal
. Другими словами:final
речь идет только о самой ссылке, а не о содержании ссылочного объекта.У Java нет понятия неизменности объекта; это достигается путем тщательного проектирования объекта, и это далеко не тривиальная задача.
источник
final
. Изменяемые объекты также могут быть размещены в стеке.final
поля могут помочь в анализе побега, но это довольно косвенный путь. Также обратите внимание, что фактически конечные переменные имеют такую же обработку, что и отмеченныеfinal
в исходном коде.final
присутствует в файле класса и имеет значительные семантические последствия для оптимизации времени выполнения. Это также может повлечь за собой затраты, поскольку JLS имеет надежную гарантию согласованностиfinal
полей объекта. Например, процессор ARM должен использовать явную инструкцию барьера памяти в конце каждого конструктора класса, который имеетfinal
поля. Однако на других процессорах это не нужно.Это любимый вопрос интервью . С помощью этих вопросов интервьюер пытается выяснить, насколько хорошо вы понимаете поведение объектов в отношении конструкторов, методов, переменных класса (статических переменных) и переменных экземпляра.
В приведенном выше случае мы определили конструктор для Test и дали ему метод setFoo.
О конструкторе: конструктор можно вызывать только один раз за создание объекта с помощью
new
ключевого слова. Вы не можете вызывать конструктор несколько раз, потому что конструктор не предназначен для этого.О методе: метод может вызываться столько раз, сколько вы хотите (даже никогда), и компилятор знает это.
Сценарий 1
foo
это экземпляр переменной. Когда мы создаемTest
объект класса, тогда переменная экземпляраfoo
будет скопирована внутри объектаTest
класса. Если мы присваиваемfoo
внутри конструктора, то компилятор знает, что конструктор будет вызываться только один раз, поэтому нет проблем с его назначением внутри конструктора.Если мы присваиваем
foo
внутри метода, компилятор знает, что метод может быть вызван несколько раз, что означает, что значение придется менять несколько раз, что недопустимо дляfinal
переменной. Таким образом, компилятор решает, что конструктор - хороший выбор! Вы можете присвоить значение конечной переменной только один раз.Сценарий 2
foo
теперь статическая переменная. Когда мы создаем экземплярTest
класса,foo
не будет скопирован в объект, потому чтоfoo
является статическим. Теперьfoo
не является независимым свойством каждого объекта. Это свойствоTest
класса. Ноfoo
может быть видно по нескольким объектам, и если каждый объект, который создается с использованиемnew
ключевого слова, в конечном итоге вызоветTest
конструктор, который изменяет значение во время создания нескольких объектов (Помните,static foo
что не копируется в каждый объект, но совместно используется несколькими объектами .)Сценарий 3
Выше
Modification-2
от вашего вопроса. В приведенном выше случае вы не изменяете первый объект, на который указывает ссылка, но вы добавляете содержимое,foo
которое разрешено. Компилятор жалуется , если вы пытаетесь присвоитьnew ArrayList()
кfoo
ссылочной переменной.Правило Если вы инициализировали
final
переменную, то вы не можете изменить ее для ссылки на другой объект. (В этом случаеArrayList
)Финальные классы не могут быть разделены на подклассы.
Финальные методы не могут быть переопределены. (Этот метод в суперклассе),
финальные методы могут переопределять. (Прочтите это грамматически. Этот метод находится в подклассе)
источник
foo
это будет установлено несколько раз, несмотря на окончательное обозначение, еслиfoo
задано в классе Test и созданы несколько экземпляров Test?foo
может быть .. несколько объектов.) Значит ли это, что если я создаю несколько объектов одновременно, то какой объект инициализирует конечную переменную, зависит от выполнения?final
адрес памяти, наfoo
который ссылается ArrayList. Вы не присваиваетеfinal
адрес памяти, на который ссылается первый элементfoo
(или любой другой элемент). Поэтому вы не можете измениться,foo
но вы можете изменитьfoo[0]
.foo = new ArrayList();
-foo
ссылается на статическую переменную, потому что мы находимся внутри одного класса.final
переменную так же, какconst
ключевое слово в C ++?Финальное ключевое слово имеет множество способов использования:
Другое использование:
Статическая переменная класса будет существовать с самого начала JVM и должна быть инициализирована в классе. Сообщение об ошибке не появится, если вы сделаете это.
источник
static
поля (если их нетfinal
) столько раз, сколько хотите. Посмотрите эту вики для разницы между назначением и инициализацией .final
Ключевое слово может быть истолковано двумя различными способами в зависимости от того, что он используется на:Типы значений: для
int
s,double
s и т. Д. Это гарантирует, что значение не может измениться,Типы ссылок: для ссылок на объекты
final
гарантирует, что ссылка никогда не изменится, а это означает, что она всегда будет ссылаться на один и тот же объект. Это не дает никаких гарантий относительно того, что значения в объекте, на который делается ссылка, остаются неизменными.Таким образом,
final List<Whatever> foo;
гарантирует, чтоfoo
всегда ссылается на один и тот же список, но содержимое этого списка может меняться со временем.источник
Если вы делаете
foo
static, вы должны инициализировать его в конструкторе класса (или в строке, где вы его определяете), как в следующих примерах.Конструктор класса (не экземпляр):
В линию:
Проблема здесь не в том, как
final
работает модификатор, а в том, какstatic
работает модификатор.final
Модификатор навязывает инициализацию справки по времени вызова конструктор Завершает (т.е. вы должны инициализировать его в конструкторе).Когда вы инициализируете атрибут в строке, он инициализируется до запуска кода, который вы определили для конструктора, так что вы получите следующие результаты:
foo
естьstatic
,foo = new ArrayList()
будет выполнен до того , как будет выполненstatic{}
конструктор, определенный вами для вашего классаfoo
нетstatic
,foo = new ArrayList()
будет выполнен до запуска вашего конструктораКогда вы не инициализируете атрибут в строке,
final
модификатор заставляет его инициализировать и делать это в конструкторе. Если у вас также естьstatic
модификатор, конструктор вам придется инициализировать атрибут является блок инициализации класса:static{}
.Ошибка, которую вы получаете в своем коде, связана с тем, что
static{}
он запускается при загрузке класса, до того, как вы создадите экземпляр объекта этого класса. Таким образом, вы не будете инициализированыfoo
при создании класса.Думайте о
static{}
блоке как о конструкторе для объекта типаClass
. Здесь вы должны выполнить инициализациюstatic final
атрибутов вашего класса (если это не сделано в строке).Примечание:
В
final
Модификатор уверяет константные-Несс только для примитивных типов и ссылок.Когда вы объявляете
final
объект, вы получаетеfinal
ссылку на этот объект, но сам объект не является константой.При объявлении
final
атрибута вы действительно достигнете того, что, как только вы объявите объект для вашей конкретной цели (напримерfinal List
, объявленный вами), этот и только этот объект будет использоваться для этой цели: вы не сможете изменить егоList foo
на другойList
, но вы все равно можете изменить свойList
, добавив / удалив элементы (то, чтоList
вы используете, будет таким же, только с измененным содержимым).источник
Это очень хороший вопрос для интервью. Иногда они могут даже спросить вас, в чем разница между конечным объектом и неизменным объектом.
1) Когда кто-то упоминает конечный объект, это означает, что ссылка не может быть изменена, но ее состояние (переменные экземпляра) может быть изменено.
2) Неизменяемый объект - это объект, состояние которого не может быть изменено, но его ссылка может быть изменена. Пример:
Переменная x может быть изменена для указания другой строки, но значение «abc» не может быть изменено.
3) Переменные экземпляра (нестатические поля) инициализируются при вызове конструктора. Таким образом, вы можете инициализировать значения переменных внутри конструктора.
4) «Но я вижу, что вы можете изменить значение в конструкторе / методах класса». - Вы не можете изменить это внутри метода.
5) Статическая переменная инициализируется во время загрузки класса. Таким образом, вы не можете инициализировать внутри конструктора, это должно быть сделано еще до этого. Поэтому вам необходимо присвоить значения статической переменной во время самого объявления.
источник
final
Ключевое слово в Java используется для ограничения пользователя.final
Ключевое слово Java может использоваться во многих контекстах. Финал может быть:final
Ключевое слово может быть применен с переменными, вfinal
переменную , которая не имеет никакого значения, называется пустымfinal
переменной или неинициализированнымfinal
переменной. Его можно инициализировать только в конструкторе. Такжеfinal
может быть пустая переменная,static
которая будет инициализирована только вstatic
блоке.Конечная переменная Java:
Если вы сделаете любую переменную
final
, вы не можете изменить значение изfinal
переменной (это будет постоянным).Пример
final
переменнойЕсть конечная переменная speedlimit, мы собираемся изменить значение этой переменной, но она не может быть изменена, потому что окончательная переменная после присвоения значения никогда не может быть изменена.
Финальный класс Java:
Если вы сделаете любой класс как
final
, вы не сможете его расширить .Пример финального класса
Финальный метод Java:
Если вы сделаете какой-либо метод финальным, вы не сможете его переопределить .
Пример
final
метода (run () в Honda не может переопределить run () в Bike)Поделиться с: http://www.javatpoint.com/final-keyword
источник
Стоит упомянуть некоторые простые определения:
Классы / Методы
переменные
final
в основном избегайте перезаписи / надписывания чем-либо (подклассы, переменная «переназначение»), в зависимости от случая.источник
final
является зарезервированным ключевым словом в Java для ограничения пользователя и может применяться к переменным-членам, методам, классам и локальным переменным. Конечные переменные часто объявляются сstatic
ключевым словом в Java и рассматриваются как константы. Например:Когда мы используем
final
ключевое слово с объявлением переменной, значение, хранящееся внутри этой переменной, не может быть изменено последним.Например:
Примечание . Класс, объявленный как final, не может быть расширен или унаследован (т. Е. Не может быть подкласса суперкласса). Также хорошо отметить, что методы, объявленные как final, не могут быть переопределены подклассами.
Преимущества использования окончательного ключевого слова рассматриваются в этой теме .
источник
the value stored inside that variable cannot be changed latter
частично верно. Это верно только для примитивных типов данных. В случае, если какой-либо объект сделан так жеfinal
, как и arraylist, его значение может измениться, но не ссылка. Спасибо!Предположим, у вас есть две копилки, красная и белая. Вы назначаете эти копилки только двум детям, и им не разрешается менять их ящики. Итак, у вас есть красные или белые копилки (окончательно), вы не можете изменить коробку, но вы можете положить деньги на свою коробку. Никого не волнует (Модификация-2).
источник
Прочитайте все ответы.
В другом пользовательском случае
final
ключевое слово может быть использовано, например, в аргументе метода:Может использоваться для переменной, которая не должна быть изменена.
источник
Когда вы делаете его статическим финалом, он должен быть инициализирован в статическом блоке инициализации
источник
final
Ключевое слово указывает на то, что переменная может быть инициализирован только один раз. В вашем коде вы выполняете только одну инициализацию final, поэтому условия выполняются. Этот оператор выполняет одиночную инициализациюfoo
. Обратите внимание, чтоfinal
! = Неизменный, это только означает, что ссылка не может измениться.Когда вы объявляете ,
foo
какstatic final
переменная должна быть инициализирована при загрузке класса и не может полагаться на конкретизации (иначе вызов конструктора) для инициализацииfoo
, поскольку статические поля должны быть доступны без экземпляра класса. Нет гарантии, что конструктор был вызван до использования статического поля.Когда вы выполняете свой метод в соответствии со
static final
сценарием,Test
класс загружается до создания экземпляра,t
в это время нет экземпляровfoo
значения, что он не был инициализирован, поэтомуfoo
для всех объектов установлено значение по умолчаниюnull
. На данный момент я предполагаю, что ваш код выдает,NullPointerException
когда вы пытаетесь добавить элемент в список.источник
Прежде всего, место в вашем коде, где вы инициализируете (т.е. присваиваете первый раз) foo, находится здесь:
foo - это объект (с типом List), поэтому это ссылочный тип, а не тип значения (например, int). Таким образом, он содержит ссылку на область памяти (например, 0xA7D2A834), где хранятся ваши элементы List. Такие линии
не меняйте значение foo (опять же, это просто ссылка на ячейку памяти). Вместо этого они просто добавляют элементы в указанное место в памяти. Чтобы нарушить последнее ключевое слово, вам нужно будет снова назначить foo следующим образом:
Это будет давать вам ошибку компиляции.
Теперь со всем этим подумайте о том, что происходит, когда вы добавляете ключевое слово static .
Если у вас нет ключевого слова static, каждый объект, который создает экземпляр класса, имеет свою собственную копию foo. Следовательно, конструктор присваивает значение пустой, свежей копии переменной foo, что совершенно нормально.
Однако, когда у вас есть ключевое слово static, в памяти существует только один foo, связанный с классом. Если бы вам нужно было создать два или более объектов, конструктор попытался бы каждый раз переназначать этот foo, нарушая ключевое слово final .
источник
final
просто привязывает ссылку к определенному объекту. Вы можете изменять «состояние» этого объекта, но не самого объекта.источник
Ниже приведены различные контексты, в которых используется final.
Конечные переменные Конечную переменную можно назначить только один раз. Если переменная является ссылкой, это означает, что переменная не может быть повторно связана с ссылкой на другой объект.
окончательной переменной может быть присвоено значение позже (не обязательно для присвоения значения при объявлении), но только один раз.
Финальные классы Финальный класс не может быть расширен (унаследован)
Финальные методы Финальный метод не может быть переопределен подклассами.
источник
Я думал написать обновленный и подробный ответ здесь.
final
Ключевое слово может быть использовано в нескольких местах.Это
final class
означает, что ни один другой класс не может расширить этот последний класс. Когда Java Run Time ( JRE ) знает, что ссылка на объект находится в типе конечного класса (скажем, F), она знает, что значение этой ссылки может быть только в типе F.Пример:
Поэтому, когда он выполняет какой-либо метод этого объекта, этот метод не требует разрешения во время выполнения с использованием виртуальной таблицы . то есть полиморфизм во время выполнения не может быть применен. Так что время выполнения не беспокоит об этом. Это означает, что это экономит время обработки, что повышает производительность.
A
final method
любого класса означает, что любой дочерний класс, расширяющий этот класс, не может переопределить этот последний метод (ы). Таким образом, поведение во время выполнения в этом сценарии также совпадает с предыдущим поведением, которое я упомянул для классов.Если кто-либо указал какой-либо вид выше как
final
, это означает, что значение уже завершено, поэтому значение не может быть изменено .Пример:
Для полей локальные параметры
Для параметров метода
Это просто означает, что значение
final
контрольного значения не может быть изменено. т.е. разрешена только одна инициализация. В этом сценарии во время выполнения, поскольку JRE знает, что значения нельзя изменить, она загружает все эти окончательные значения (окончательных ссылок) в кэш L1 . Потому что это не нужно , чтобы загрузить обратно снова и снова из основной памяти . В противном случае он загружается в кэш L2 и время от времени загружается из основной памяти. Так что это также улучшение производительности.Таким образом, во всех вышеупомянутых 3 сценариях, когда мы не указали
final
ключевое слово в местах, которые мы можем использовать, нам не нужно беспокоиться, оптимизация компилятора сделает это за нас. Есть также много других вещей, которые оптимизация компилятора делает для нас. :)источник
Прежде всего, это правильно. Кроме того, если вы не хотите, чтобы другие создавали подклассы из вашего класса, объявите ваш класс как final. Затем он становится конечным уровнем иерархии дерева классов, и никто не может его расширить. Это хорошая практика, чтобы избежать огромной иерархии классов.
источник