我在我正在处理的代码库中遇到了以下代码:
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的使用无效。 (它没有)。
我假设答案必须用指针和记忆做一些事情但是想肯定地知道。
答案 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
变量包含对数组的引用,则数组的组件可能会被数组上的操作更改,但变量将始终引用相同的数组。
以下是making an object immutable的指南。
答案 5 :(得分:4)
最终和永恒不是一回事。最后意味着不能重新分配引用,所以你不能说
INSTANCE = ...
不可变意味着对象本身无法修改。一个例子是java.lang.String
类。您无法修改字符串的值。
答案 6 :(得分:2)
Java没有内置于该语言中的不可变性概念。没有办法将方法标记为mutator。因此,语言无法强制实现对象不变性。