3

Effective Java中,Bloch 建议在使对象不可变时使所有字段最终化。

有必要这样做吗?不会只是不提供访问器方法使其不可变。

例如

class A {
      private int x;
      A (int x) {
          this.x = x;
      }
}

即使我没有声明xfinal正确,上面的类也是不可变的?我错过了什么吗?

4

7 回答 7

6

除了@Bozho 的观点之外,将字段声明为final意味着可以安全地访问它而无需任何同步。

相比之下,如果该字段不final存在,则如果另一个线程在没有正确同步的情况下访问该字段,则该字段的异常值的风险很小。即使在对象构造后没有改变字段的值,也会发生这种情况!

于 2011-09-29T13:49:46.357 回答
5

它不是“完全”不可变的,因为您可以更改值。接下来的事情将是团队中的其他人为该字段分配一个新值。final表示不变性的意图。

于 2011-09-29T13:45:36.313 回答
3

正如Java Concurrency In Practice定义的那样,“有效不可变”类定义了该术语。

这意味着,只要对实例的引用是“安全发布的”,它们就是不可变的。安全地发布引用涉及使用同步,以便 Java 内存模型 (JMM) 可以保证调用者将看到完全写入的字段值。例如,如果该字段不是最终的,并且构造了一个实例并将其传递给另一个线程,则另一个线程可能会看到该字段处于未定义状态(例如,null如果它是对象引用,或者只是 64 位long字段的一半)。

如果实例仅在单个线程中使用,则区别无关紧要。这是因为 JMM 使用“线程内如同串行”语义。因此,构造函数中字段的赋值总是发生在可以读取该字段之前。

如果该字段是final,JMM 将保证调用者将看到正确的值,无论引用是如何发布的。final如果您想在不使用同步形式的情况下将实例传递给其他线程,那么这样做是有好处的。

于 2011-10-01T09:51:33.670 回答
2

您也可以这样做,但是当您将其声明为 final 时,编译器会为您提供帮助。一旦您尝试为成员变量分配新值,编译器就会抛出错误。

于 2011-09-29T13:47:50.413 回答
2

是的,就目前的形式而言,这个类是不可变的。当然忽略反射。

然而,正如@Bozho 所说,只需要有人添加一种方法来改变它。

使 x final 提供额外的安全性并使您的意图清晰。

于 2011-09-29T13:49:23.023 回答
2

您始终可以使用 setAccessible 设置私有字段。这就是 Spring、Hibernate 和其他此类框架的运行方式。如果也可以对 A 进行子类化,那么这就提出了一个问题,即 A 的所有实例是否都是不可变的。

明确不变性的主要好处是它使编码器的意图变得清晰。final 不能有 setter,因此阅读代码的人不必寻找它。我通常还在课堂评论中说明不变性的意图。

(我假设您通常知道不变性的好处,因为您只询问了机制。)

于 2011-09-29T13:49:59.747 回答
0

除了可以添加更改非最终字段值的代码之外,JVM 对最终字段和非最终字段的处理方式不同。Java 内存模型有一个关于这个主题的部分(非常不随意的阅读)。

于 2011-09-29T13:54:14.450 回答