Я наткнулся на следующий код в кодовой базе, над которой работаю:
public final class ConfigurationService {
private static final ConfigurationService INSTANCE = new ConfigurationService();
private List providers;
private ConfigurationService() {
providers = new ArrayList();
}
public static void addProvider(ConfigurationProvider provider) {
INSTANCE.providers.add(provider);
}
...
INSTANCE
объявлен как final
. Почему можно добавлять объекты INSTANCE
? Разве это не означает, что использование final. (Это не так).
Я предполагаю, что ответ должен что-то делать с указателями и памятью, но хотел бы знать наверняка.
Ответы:
final
просто делает ссылку на объект неизменной. При этом объект, на который он указывает, не является неизменным.INSTANCE
никогда не может ссылаться на другой объект, но объект, на который он ссылается, может изменить состояние.источник
ConfigurationService
объект и пытаюсь выполнить INSTANCE =deserializedConfigurationService
not be allowed?INSTANCE
ссылку на другой объект. Неважно, откуда появился другой объект. (NB есть одинINSTANCE
наClassLoader
который загружен этот класс Вы могли бы в теории нагрузки классовые несколько раз в одной виртуальной машины Java и каждый отдельный Но это другой, технический момент...)Быть окончательным - не то же самое, что быть неизменным.
final != immutable
final
Используется ключевое слово , чтобы убедиться , что ссылка не меняется (то есть ссылка она не может быть заменен на новый)Но если атрибут является самодифицируемым, можно делать то, что вы только что описали.
Например
class SomeHighLevelClass { public final MutableObject someFinalObject = new MutableObject(); }
Если мы создадим экземпляр этого класса, мы не сможем присвоить атрибуту другое значение,
someFinalObject
потому что оно окончательное .Так что это невозможно:
.... SomeHighLevelClass someObject = new SomeHighLevelClass(); MutableObject impostor = new MutableObject(); someObject.someFinal = impostor; // not allowed because someFinal is .. well final
Но если сам объект изменен следующим образом:
class MutableObject { private int n = 0; public void incrementNumber() { n++; } public String toString(){ return ""+n; } }
Затем значение, содержащееся в этом изменяемом объекте, может быть изменено.
SomeHighLevelClass someObject = new SomeHighLevelClass(); someObject.someFinal.incrementNumber(); someObject.someFinal.incrementNumber(); someObject.someFinal.incrementNumber(); System.out.println( someObject.someFinal ); // prints 3
Это имеет тот же эффект, что и ваш пост:
public static void addProvider(ConfigurationProvider provider) { INSTANCE.providers.add(provider); }
Здесь вы не меняете значение INSTANCE, вы изменяете его внутреннее состояние (с помощью метода provider.add)
если вы хотите предотвратить изменение определения класса следующим образом:
public final class ConfigurationService { private static final ConfigurationService INSTANCE = new ConfigurationService(); private List providers; private ConfigurationService() { providers = new ArrayList(); } // Avoid modifications //public static void addProvider(ConfigurationProvider provider) { // INSTANCE.providers.add(provider); //} // No mutators allowed anymore :) ....
Но это может не иметь большого смысла :)
Кстати, синхронизировать доступ к нему тоже нужно в основном по той же причине.
источник
Ключ к недоразумению - в заголовке вашего вопроса. Конечен не объект , а переменная . Значение переменной не может измениться, но данные в ней могут.
Всегда помните, что когда вы объявляете переменную ссылочного типа, значение этой переменной является ссылкой, а не объектом.
источник
final означает, что ссылку нельзя изменить. Вы не можете переназначить INSTANCE другой ссылке, если она объявлена как final. Внутреннее состояние объекта остается изменчивым.
final ConfigurationService INSTANCE = new ConfigurationService(); ConfigurationService anotherInstance = new ConfigurationService(); INSTANCE = anotherInstance;
выдаст ошибку компиляции
источник
Источник
Вот руководство по созданию неизменяемого объекта .
источник
Окончательное и неизменное - не одно и то же. Окончательный означает, что ссылку нельзя переназначить, поэтому вы не можете сказать
Неизменяемость означает, что сам объект не может быть изменен. Примером этого является
java.lang.String
класс. Вы не можете изменить значение строки.источник
В языке Java нет встроенной концепции неизменяемости. Невозможно пометить методы как мутаторы. Следовательно, в языке нет способа обеспечить неизменность объекта.
источник