Почему конечный объект может быть изменен?


я наткнулся на следующий код в базе кода, над которой я работаю:

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);
    }

    ...

экземпляр объявляется как окончательный. Почему объекты могут быть добавлены в экземпляр? Не должно ли это сделать недействительным использование final. (Это не так).

Я предполагаю, что ответ должен что-то делать с указателями и памятью, но хотелось бы знать точно.

8 65

8 ответов:

'final' просто делает объект ссылка неизменяем. Объект на который он указывает не является неизменным при этом. Экземпляр никогда не может ссылаться на другой объект, но объект, на который он ссылается, может изменить состояние.

быть окончательным-это не то же самое, что быть неизменным.

final != immutable

The final ключевое слово используется, чтобы убедиться, что ссылка не изменена ( то есть ссылка, которую она имеет, не может быть заменена новой )

но, если атрибут self является модифицируемым, это нормально делать то, что вы только что описали.

например

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);
}

здесь вы не меняете значение экземпляра, вы изменяете его внутреннее состояние (via, providers.метод 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 ConfigurationService INSTANCE = new ConfigurationService();
ConfigurationService anotherInstance = new ConfigurationService();
INSTANCE = anotherInstance;

вызовет ошибку компиляции

после final переменная была назначена, она всегда содержит одно и то же значение. Если a final переменная содержит ссылку на объект, то состояние объекта может быть изменено операциями на объекте, но переменная будет всегда ссылаться на один и тот же объект. Это относится также к массивам, поскольку массивы являются объектами; если a final переменная содержит ссылку на массив, тогда компоненты массива могут быть изменены операциями над массивом, но переменная всегда будет обратитесь к тому же массиву.

источник

вот руководство о сделать объект неизменяемым.

окончательное и неизменное-это не одно и то же. Final означает, что ссылка не может быть переназначена, поэтому вы не можете сказать

INSTANCE = ...

непреложно означает, что сам объект не может быть изменен. Примером этого является java.ленг.Класс string. Вы не можете изменить значение строки.

Java не имеет концепции неизменяемости, встроенной в язык. Нет никакого способа пометить методы как мутатор. Поэтому язык не имеет возможности обеспечить неизменность объекта.

Если вы используете ключевое слово final для объекта, хэш-код никогда не изменится, что делает его неизменным. Он будет использоваться в одноэлементном шаблоне.

class mysingleton1{

private mysingleton1(){}
public static final mysingleton1 instance= new mysingleton1();

    public static mysingleton1 getInstance(){

        return instance;

    }

}