Итак, мой вопрос в том, почему они гарантируют видимость и упорядочение с использованием конечной переменной
Потому что они определили это таким образом, и теперь задача виртуальной машины - придерживаться этого определения .
Конечно, следующий вопрос: почему они так определили это?
Это из-за странной «особенности» модели памяти Java. Рассмотрим этот пример.
class Foo {
int x;
volatile int y;
Foo() {
x = 3;
y = 4;
}
static final Foo INSTANCE;
static {
INSTANCE= new Foo();
}
}
Инициализатор stati c будет скомпилирован в этот (упрощенный псевдокод):
Foo tmp = allocate(Foo.class)
Foo.INSTANCE = tmp
tmp.x = 3
tmp.y = 4
Как видите, экземпляр сделан publi c перед выполнением конструктора volatile
здесь ничего не меняет.
Такое поведение неожиданно для большинства разработчиков и может привести к очень сложным для отладки ошибкам. Таким образом, чтобы немного уменьшить масштабы этой проблемы, спецификация была скорректирована таким образом, чтобы требовать инициализации полей final перед публикацией экземпляра c.
Пример сверху, с объявлением x
final
.
Foo tmp = allocate(Foo.class)
tmp.x = 3
Foo.INSTANCE = tmp
tmp.y = 4