93

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

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?那不应该使最终的使用无效。(它没有)。

我假设答案必须与指针和内存有关,但我想确定。

4

7 回答 7

174

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

于 2010-03-12T19:19:33.780 回答
35

最终化不等于不可变。

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 :) 
....

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

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

于 2010-03-12T19:38:04.770 回答
27

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

永远记住,当你声明一个引用类型变量时,该变量的值是一个引用,而不是一个对象。

于 2010-03-12T19:22:08.647 回答
13

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

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

会抛出编译错误

于 2010-03-12T19:24:20.787 回答
7

final分配变量后,它始终包含相同的值。如果final变量持有对对象的引用,则对象的状态可能会通过对对象的操作而改变,但变量将始终引用同一个对象。这也适用于数组,因为数组是对象;如果final变量持有对数组的引用,则数组的组成部分可能会通过对数组的操作而改变,但变量将始终引用同一个数组。

来源

这是使对象不可变的指南。

于 2010-03-12T19:26:41.690 回答
5

final 和 immutable 不是一回事。最终意味着不能重新分配参考,所以你不能说

INSTANCE = ...

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

于 2010-03-12T19:20:33.687 回答
2

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

于 2010-03-12T19:51:09.170 回答