Действительно ли это верно, что присвоенное поле конечного объекта может все еще быть пустым в конструкторе?
class MyClass {
private final Object obj = new Object();
public MyClass() {
System.out.println(obj); // may print null?
}
}
если да, разве это не ошибка?
Как обсуждалось в других ответах, нет, этого не может быть.Однако с назначенным конечным статическим полем это возможно.
class MyClass {
private static MyClass myClass = new MyClass();
private static final Object obj = new Object();
public MyClass() {
System.out.println(obj); // will print null once
}
}
Это невозможно, поскольку все инициализаторы выполняются до вызова конструктора.
Инициализаторы переменных, как у вас private final Object obj = new Object();
запускаются до вызова конструктора. Это также справедливо для инициализационных блоков, статических или иных.
При использовании инициализаторов следует обратить внимание на то, что инициализаторы не могут делать прямые ссылки Когда вы пишете инициализатор, вы не можете ссылаться на любые переменные экземпляра, объявленные текстуально после инициализируемой переменной.
На таком простом примере, как ваш, ничего плохого случиться не может. Однако возможно, чтобы поле final
отображалось как неинициализированное, если вы используете сомнительные методы, такие как вызов переопределяемого метода в конструкторе.
Например, следующая программа печатает «Мой любимый цвет - ноль», даже несмотря на то, что она ссылается на последнюю переменную favouriteColour
, для которой в конструкторе установлено значение «синий»
.
abstract class SuperClass {
final String favouriteColour;
SuperClass() {
announceFavouriteColour();
favouriteColour = "blue";
}
abstract void announceFavouriteColour();
}
public class FinalTest extends SuperClass {
void announceFavouriteColour() {
System.out.println("My favourite colour is " + favouriteColour);
}
public static void main(String[] args) {
new FinalTest();
}
}
У меня работает:
$ cat MyClass.java
class MyClass {
private final Object obj = new Object();
public MyClass() {
System.out.println(obj); // may print null?
}
public static void main(String[] args) { new MyClass(); }
}
$javac MyClass.java; java MyClass
java.lang.Object@19908ca1
Все инициализаторы полей копируются компилятором в начало всех конструкторов.
Однако в модели памяти Java 5, если вы позволите ссылке this
«уйти» до конца конструктора, другие потоки смогут увидеть неинициализированные значения полей final
( так что в этом случае можно увидеть null
).
Инициализатор Object obj = new Object ();
будет запускаться перед кодом внутри конструктора, поэтому obj
не может иметь значение null
.
Обратите внимание, что это не будет компилироваться, если вы нигде не инициализировали obj
.