2

我有一个不可变对象,它的字段或类没有标记为 final。我可以这样做,但是这样做真的有什么好处吗?我可以看到它为编译器节省了一点时间来解决问题,但我看不出它是“值得的”(除此之外,它会让未来的开发人员重新考虑对对象做一些事情以使其可变) .

4

4 回答 4

3

除了您提出的观点(未来的开发人员修改字段是非常明智的,另一个是有人可以子类化您的类并使其可变),明确地将字段标记为 final 为您提供了多线程环境中的可见性保证。

参加这个课程 - 它实际上是不可变的:

public class SomeClass {
    private int i;
    public SomeClass(int i) { this.i = i; }
    public int getI() { return this.i; }
}

在多线程环境中,有可能一个线程 T1 创建了 a SomeClass sc = new SomeClass(1);,而另一个线程 T2 读取sc.getI()并看到了 0。

如果设置i为最终状态,则不会再发生这种情况(假设您在构建过程中不让this逃跑,如下面的引文中所述)。

参考:JLS #17.5 - 强调我的

final 字段还允许程序员在不同步的情况下实现线程安全的不可变对象。[...]
final 字段的使用模型很简单:在对象的构造函数中设置对象的 final 字段;并且不要在对象的构造函数完成之前在另一个线程可以看到它的地方写入对正在构造的对象的引用。如果遵循这一点,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本

于 2012-08-16T17:05:15.817 回答
1

它用于显示您的意图,而不是让某人错误地使其可变。
另外将方法标记为最终方法可能会成为内联方法,从而获得性能优势。
还将类的引用对象标记为final强制构造函数是原子的。最后将类标记为final停止继承。这是你需要的吗?

于 2012-08-16T17:04:27.990 回答
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;
   }
}

子类完全覆盖了该字段,使其可变。您所要做的就是强制转换 to 的实例MyClassMySubClass访问设置器。即使没有 setter,子类也可以num在另一个方法中更改其字段。

假设该字段是私有的,则该字段是否无关紧要final:您需要创建 classfinal或创建 getter final


还要注意,即使有你喜欢的所有代码保护,使用反射其他类仍然可以穿透所有内容并改变你的字段。

于 2012-08-16T17:25:04.850 回答
0

首先,final字段与类非常不同final。最终字段无法更改。类上的final修饰符意味着不允许其他类从该类继承,但对字段完全没有影响。

话虽如此,将字段标记为 final 确实允许编译器进行一些优化。这些影响有多大取决于很多。如果您的程序中没有性能问题,那么它可能不值得。

但是在设计方面,如果它们打算成为最终的,则将它们标记为最终是有用的,因此使用您的类的任何人都不会无意中破坏某些东西。因此,总的来说,如果您有一些设计后不想更改的内容,请将其定为最终版本。

于 2012-08-16T17:06:16.073 回答