我目前正在阅读here,为了实现对象的不变性,我必须:
如果仍然没有设置器,为什么需要将字段声明为final。 Java编译器不允许这样的内容:
myObj.getSomething() = new Somthing()
如果我尝试使用反射,那么final
关键字不会阻止我更改参考。
我找到了一个很好的解释here,为什么整个类都需要final
,但是为什么私有字段必须是最终的却一无所获。
编辑: 回复GotoFinal的comment 这是一个展示如何使用反射编辑字段的类:
public class Test {
static class Immutable {
private final StringBuilder immutableField = new StringBuilder("You can't set final field just by normal reflections");
public StringBuilder getStringBuilder() {
return immutableField;
}
}
public static void main(String[] args) throws Exception {
Immutable immutableObject = new Immutable();
Field f1 = immutableObject.getClass().getDeclaredField("immutableField");
f1.setAccessible(true);
f1.set(immutableObject, new StringBuilder("Well, I just did"));
System.out.println(immutableObject.getStringBuilder());
}
}
答案 0 :(得分:4)
它不一定是最终的,但这是一个好习惯,因为在阅读源代码时,您立即知道给定的字段不能更改-包括类内部或本地类-在您的类内部声明(java将生成然后是该领域的特殊桥接设置器。
另外,由于field是final,因此您需要在构造函数中对其进行初始化-因此,在此处很难出错。
它也可能会影响反序列化器的工作方式,因为大多数库都不会尝试修改最终字段。
答案 1 :(得分:0)
为防止字段被重新分配,您应将其定为最终字段。
仅定义定义器的策略是不够的,因为:
将字段声明为final可以防止这两种情况。另外,检查final关键字然后阅读整个类以验证是否没有setter会容易得多。
此外,如果将该字段声明为final,那么JVM可以应用各种优化。 我会说反射在这里是无关紧要的,因为使用它可以绕过任何事情。
答案 2 :(得分:0)
不可变类的主要优点之一是它们本质上是线程安全的:如果它们没有可变状态,则一个线程不可能影响另一个线程看到的该类实例的行为。 / p>
声明字段final
实际上会对该字段的可见性产生影响(在Java内存模型的意义上,不是public
/ private
)。规范保证声明为final
的字段在构造函数完成执行之前被初始化。
这意味着-假设您在构造函数完成之前没有不安全地发布对实例的引用-保证所有线程都可以看到实例的final
字段的初始化值。
没有final
,就没有这样的保证,这意味着实例不是 truly 不可变的-线程可能一次从实例中读取一个值,然后一个与另一个实例的值不同。