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

时间:2010-03-12 19:17:27

标签: 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);
    }

    ...

INSTANCE被声明为final。为什么可以将对象添加到INSTANCE?不应该使final的使用无效。 (它没有)。

我假设答案必须用指针和记忆做一些事情但是想肯定地知道。

7 个答案:

答案 0 :(得分:148)

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

答案 1 :(得分:33)

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

final != immutable

final关键字用于确保引用不会更改(即,它的引用不能替换为新引用)

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

例如

class SomeHighLevelClass {
    public final MutableObject someFinalObject = new MutableObject();
}

如果我们实例化此类,我们将无法为属性someFinalObject指定其他值,因为它是 final

所以这是不可能的:

....
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的值,您正在修改其内部状态(通过,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 :) 
....

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

顺便说一下,基本上出于同样的原因你也必须synchronize access to it

答案 2 :(得分:24)

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

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

答案 3 :(得分:11)

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

final ConfigurationService INSTANCE = new ConfigurationService();
ConfigurationService anotherInstance = new ConfigurationService();
INSTANCE = anotherInstance;

会抛出编译错误

答案 4 :(得分:6)

  

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

Source

以下是making an object immutable的指南。

答案 5 :(得分:4)

最终和永恒不是一回事。最后意味着不能重新分配引用,所以你不能说

INSTANCE = ...

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

答案 6 :(得分:2)

Java没有内置于该语言中的不可变性概念。没有办法将方法标记为mutator。因此,语言无法强制实现对象不变性。