final
объявляет ссылку на объект, который не может быть изменен, например,
private final Foo something = new Foo();
создает новый Foo
и помещает ссылку в something
. После этого невозможно изменить something
, чтобы указать на другой экземпляр Foo
.
Это не предотвращает изменение внутреннего состояния объекта. Я все еще могу вызывать любые методы на Foo
, доступные для соответствующей области. Если один или несколько из этих методов изменят внутреннее состояние этого объекта, final
не предотвратит это.
Таким образом, следующее:
private final Set<String> fixed = new HashSet<String>();
не не создает Set
, который не может быть добавлен или иным образом изменен; это просто означает, что fixed
будет ссылаться только на этот экземпляр.
В отличие от этого:
private Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );
создает экземпляр Set
, который выдаст UnsupportedOperationException
, например, если попытаться вызвать fixed.add()
или fixed.remove()
, - сам объект защитит свое внутреннее состояние и предотвратит его изменение.
Ради полноты:
private final Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );
создает экземпляр Set
, который не позволяет изменять его внутреннее состояние, а также означает, что fixed
будет указывать только на экземпляр этого набора.
Причина, по которой final
может использоваться для создания констант примитивов, основана на том факте, что значение не может быть изменено. Помните, что fixed
выше было просто ссылкой - переменной, содержащей адрес, который нельзя изменить. Ну, для примитивов, например
private final int ANSWER = 42;
значение ANSWER
равно 42. Поскольку ANSWER
нельзя изменить, оно будет иметь только значение 42.
Пример, который стирает все строки, будет следующим:
private final String QUESTION = "The ultimate question";
Согласно приведенным выше правилам, QUESTION
содержит адрес экземпляра String
, который представляет собой «окончательный вопрос», и этот адрес не может быть изменен. Здесь следует помнить, что String
сам по себе неизменен - вы ничего не можете сделать с экземпляром String
, который его изменяет, и с любыми другими операциями, которые могли бы сделать это (например, replace
, substring
и т.д.) возвращать ссылки на совершенно разные экземпляры String
.