本文介绍了Java中的不变性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我的类将是一个不可变的,它必须是 final 并且没有任何修改其状态的方法,并且所有属性必须是私有的。但为什么它应该声明为 final 的属性(例如, private final int a )?

If my class is going to be an immutable one it has to be final and without any method that modifies its state and all properties must be private. But why should it have properties declared as final (for example, private final int a)?

编辑

如果类具有对象的引用,那么该类是否仍然是不可变的不可变吗?

Will the class still be an immutable one if it has a reference to objects that are not immutable?

推荐答案

来自Jeremy Manson的博文:

From Jeremy Manson's blog post Immutability in Java:

Manson共同撰写了Java Memory Model规范,所以应该知道他在说什么。

Manson co-authored the Java Memory Model spec, so should be knowing what he is talking about.

Brian Goetz的预订:

An example from the Brian Goetz's Java Concurrency in Practice book:

public Holder holder;
public void initialize() { holder = new Holder(42); }

publc class Holder {
  private int n;
  public Holder(int n) { this.n = n; }

  public void assertSanity() {
    if (n != n) {
      throw new AssertionError("This statement is false");
    }
  }
}

// Thread 1:             // Thread 2:
initialize();            if (holder != null) holder.assertSanity();

上面的线程2可以抛出 AssertionError 。为避免此类可见性问题, 字段类的字段应已声明为 final

Thread 2 above can throw the AssertionError. To avoid such visibility issues the field n of Holder class should have been declared final.

编辑:考虑一系列步骤:

Consider the sequence of steps:


  1. 类型的新对象分配内存

  2. 初始化字段 n 新对象

  3. 执行构造函数

  4. 将新对象的引用分配给 holder 参考

  1. Allocate memory for new object of type Holder
  2. Initialize field n of the new object
  3. Execute the constructor
  4. Assign the reference of the new object to holder reference

这是理智的事件序列,JMM(Java内存模型)保证这是线程调用 initialize()会看到。但是,由于没有同步,JMM不保证调用 holder.assertSanity()的第二个线程会看到什么序列。特别是,如果第二个线程看到序列,则抛出 AssertionError

This is the "sane" sequence of events and the JMM (Java Memory Model) guarantees that this is what the thread calling initialize() will see. But, since there is no synchronization, JMM makes no guarantees about what sequence the second thread calling holder.assertSanity() will see. In particular, the AssertionError will be thrown if the second thread sees the sequence:


  1. holder 非null,因此可以调用 assertSanity 方法

  2. 第一次读取 n 结果为0,默认值为 int 字段

  3. 第二次读取 n 结果42

  1. holder is non-null, so that it could call assertSanity method
  2. The first read of n results in 0, the default value of an int field
  3. The second read of n results 42

简而言之,当一个对象的构造函数在一个线程中返回时,另一个线程可能看不到该对象的所有字段初始化。为了保证第二个线程看到正确初始化的字段,您应该使它们 final ,或者执行互斥锁解锁(即 initialize()方法应该是 synchronized )或者做一个 volatile 写(即持有者应为 volatile )。

In short, when an object's constructor returns in one thread, another thread might not see all field initializations of that object. To guarantee that the second thread sees the correctly initialized fields, you should either make them final, or do a mutex unlock (i.e. initialize() method should be synchronized) or do a volatile write (i.e. holder should be volatile).

这篇关于Java中的不变性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-16 13:36
查看更多