为什么最终对象可以修改?

Mat*_*ick 82 java final

我在我正在处理的代码库中遇到以下代码:

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

    ...
Run Code Online (Sandbox Code Playgroud)

INSTANCE被宣布为final.为什么可以添加对象INSTANCE?不应该使final的使用无效.(它没有).

我假设答案必须用指针和内存做一些事情,但我想肯定知道.

Sea*_*wen 154

final只是使对象引用不可更改.它指向的对象不是一成不变的.INSTANCE永远不能引用另一个对象,但它引用的对象可能会改变状态.


Osc*_*Ryz 33

最终与不可变是不一样的.

final != immutable

所述final关键字被用于确保基准不会改变(即,它已经不能用新的被取代的参考)

但是,如果属性是self是可修改的,那么你可以按照你刚才描述的那样做.

例如

class SomeHighLevelClass {
    public final MutableObject someFinalObject = new MutableObject();
}
Run Code Online (Sandbox Code Playgroud)

如果我们实例化这个类,我们将无法为该属性分配其他值,someFinalObject因为它是最终的.

所以这是不可能的:

....
SomeHighLevelClass someObject = new SomeHighLevelClass();
MutableObject impostor  = new MutableObject();
someObject.someFinal = impostor; // not allowed because someFinal is .. well final
Run Code Online (Sandbox Code Playgroud)

但如果它自己的对象是可变的,就像这样:

class MutableObject {
     private int n = 0;

     public void incrementNumber() {
         n++;
     }
     public String toString(){
         return ""+n;
     }
}  
Run Code Online (Sandbox Code Playgroud)

然后,可以改变该可变对象包含的值.

SomeHighLevelClass someObject = new SomeHighLevelClass();

someObject.someFinal.incrementNumber();
someObject.someFinal.incrementNumber();
someObject.someFinal.incrementNumber();

System.out.println( someObject.someFinal ); // prints 3
Run Code Online (Sandbox Code Playgroud)

这与你的帖子有同样的效果:

public static void addProvider(ConfigurationProvider provider) {
    INSTANCE.providers.add(provider);
}
Run Code Online (Sandbox Code Playgroud)

这里你没有改变INSTANCE的值,你正在修改它的内部状态(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 :) 
....
Run Code Online (Sandbox Code Playgroud)

但是,它可能没有多大意义:)

顺便说一句,您还必须基本上以同样的原因同步对它的访问.


Jon*_*eet 24

误解的关键在于你的问题标题.这不是最终的对象,而是变量.变量的值不能改变,但其中的数据可以改变.

永远记住,当您声明引用类型变量时,该变量的值是引用,而不是对象.


小智 11

final只是意味着引用无法更改.如果将INSTANCE声明为final,则无法将其重新分配给另一个引用.对象的内部状态仍然是可变的.

final ConfigurationService INSTANCE = new ConfigurationService();
ConfigurationService anotherInstance = new ConfigurationService();
INSTANCE = anotherInstance;
Run Code Online (Sandbox Code Playgroud)

会抛出编译错误


def*_*alt 6

一旦一个final变量被分配,它总是包含相同的值.如果final变量包含对对象的引用,则可以通过对对象的操作来更改对象的状态,但该变量将始终引用同一对象.这也适用于数组,因为数组是对象; 如果final变量保存对数组的引用,则数组的组件可能会被数组上的操作更改,但变量将始终引用相同的数组.

资源

这是一个使对象不可变的指南.


Chr*_*ail 5

最终和不可变不是一回事。Final 意味着引用不能被重新分配,所以你不能说

INSTANCE = ...
Run Code Online (Sandbox Code Playgroud)

不可变意味着对象本身不能被修改。类就是一个例子java.lang.String。您无法修改字符串的值。