我有一个不可变对象,没有将其字段或类标记为final。我能做到这一点,但这样做真的有什么好处吗?我可以看到它节省编译器一点点时间来解决问题,但我不能看到它“值得”它(其他事实是它将使未来的开发人员重新思考对对象做某事使其变得可变)
答案 0 :(得分:3)
除了你提出的观点之外(未来的开发人员修改字段是非常明智的,另一个是有人可以将你的类子类化并使其变为可变),明确地将字段标记为final可以在多个中提供可见性保证线程环境。
拿这个课 - 它实际上是不可变的:
public class SomeClass {
private int i;
public SomeClass(int i) { this.i = i; }
public int getI() { return this.i; }
}
在多线程环境中,线程T1可能会创建SomeClass sc = new SomeClass(1);
,而另一个线程T2可能会读取sc.getI()
并看到0。
如果i
成为最终版,则不会再发生这种情况(假设您在施工期间不让this
逃脱,如下面引用中所述)。
参考:JLS #17.5 - 强调我的
final字段还允许程序员在没有同步的情况下实现线程安全的不可变对象。 [...]
final字段的用法模型很简单:在该对象的构造函数中设置对象的最终字段;并且在对象的构造函数完成之前,不要在另一个线程可以看到的地方写入对正在构造的对象的引用。如果遵循此规则,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本。
答案 1 :(得分:1)
它可以显示你的意图,而不是让某人误将其变为可变
将方法标记为最终可能成为内联获得性能优势
同时将类的引用对象标记为final
会强制构造函数为原子。
最后将类标记为final
会停止继承。这是你需要的吗?
答案 2 :(得分:1)
制作字段final
更多的是显示代码的意图。
除非你使用getter方法final
,否则你的类不是不可变的。考虑:
public class MyClass {
private final int num;
public int getNum() {
return num;
}
}
public class MySubClass extends MyClass {
private int num;
public int getNum() {
return num;
}
public void setNum(int i) {
num = i;
}
}
子类完全覆盖了字段,使其变得可变。您所要做的就是将MyClass
的实例强制转换为MySubClass
以获取对setter的访问权限。即使没有setter,子类也可以在另一种方法中更改其num
字段。
假设该字段是私有字段,则字段为final
并不重要:您需要创建课程final
,或者制作获取者final
。
另请注意,即使您喜欢所有代码保护,使用反射其他类仍然可以切换所有内容并改变您的字段。
答案 3 :(得分:0)
首先,final
字段与final
类非常不同。最终字段无法更改。类上的final
修饰符意味着不允许其他类从该类继承,但根本不会对这些字段产生任何影响。
话虽这么说,标记为final的字段确实允许编译器进行一些优化。这些影响有多大取决于很多。如果您的程序中没有性能问题,则可能不值得。
但是在设计方面,如果它们是最终的,那么将事物标记为最终是有用的,所以没有人使用你的课程会无意中破坏某些东西。总的来说,如果你有一些你设计的东西不想在以后改变,那就把它作为最终的。