JLS 17.5中,有如下规定:
An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.
即为了保证final字段的值是对的,引用的赋值必须得在这个对象完全初始化后来做。
也就是说,不管是final还是非final字段,在这个这个引用赋值之前就可见了。
但是,JLS紧接着一个例子:
class FinalFieldExample {
final int x;
int y;
static FinalFieldExample f;
public FinalFieldExample() {
x = 3;
y = 4;
}
static void writer() {
f = new FinalFieldExample();
}
static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // could see 0
}
}
}
The class FinalFieldExample has a final int field x and a non-final int field y. One thread might execute the method writer and another might execute the method reader.
也就是说,只保证final字段可见,而不保证非final字段了。
请问,这两点如何理解?是JLS前后规定不一致吗?
这个推论是不成立的。 JLS 一定要把 final 字段单独拿出来说,就是因为非 final 字段是没有这个性质的。上面的引用仅讨论了 final 字段,与非 final 字段无关。
即使是完全初始化之后,对非 final 字段,在另一个线程,可能也看不到“正确”的初始化值。