Почему поля, кажется, инициализируются перед конструктором?
public class Dog {
public static Dog dog = new Dog();
static final int val1 = -5;
static int val2 = 3;
public int val3;
public Dog() {
val3 = val1 + val2;
}
public static void main(String[] args) {
System.out.println(Dog.dog.val3);
}
}
Выходной сигнал -5
Из этого результата кажется, что инициализация val2
выполняется до завершения члена dog
и его экземпляра.
Почему этот порядок выглядит следующим образом?
Ответы
Ответ 1
Если вы переместите экземпляр вашей собаки в конце, вы увидите, что результат будет равен -2
public class Dog {
static final int val1 = -5;// This is final, so will be initialized at compile time
static int val2 = 3;
public int val3;
public static Dog dog = new Dog();//move to here
public Dog() {
val3 = val1 + val2;
}
public static void main(String[] args) {
System.out.println(Dog.dog.val3);//output will be -2
}
}
Окончательные поля (значения которых являются константами постоянной времени компиляции) сначала будут инициализированы, а затем остальные будут выполнены в текстовом порядке.
Итак, в вашем случае, когда инициализируется экземпляр собаки, static int val2
(0) еще не инициализирован, тогда как static final int val1
(- 5) делает, поскольку он является окончательным.
http://docs.oracle.com/javase/specs/jls/se5.0/html/execution.html#12.4.2 утверждает, что:
выполнить либо инициализаторы переменной класса, либо статические инициализаторы класса или инициализаторы полей интерфейса, в текстовом порядке, как если бы они были одним блоком, , за исключением того, что конечные переменные класса и поля интерфейсов, значения которых константы времени компиляции сначала инициализируются
Обновлен новый документ
Вот версия jdk7 из http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2
Конечное поле находится на шаге 6:
Затем инициализируйте конечные переменные класса и поля интерфейсов значения которых являются постоянными выражениями времени компиляции
тогда как статическое поле находится на шаге 9:
Далее выполните либо инициализаторы переменной класса, либо статические инициализаторы класса или инициализаторы полей интерфейса, в текстовом порядке, как если бы они были одним блоком.
Ответ 2
Переменная последовательность объявлений. static final int val1
сначала инициализируется, потому что это константа. Однако static int val2
все еще 0
в момент создания public static Dog dog = new Dog();
.
Ответ 3
Что происходит..
Первая строка, которая выполняется, - это public static Dog dog = new Dog();
.
Теперь есть две вещи, которые нужно иметь в виду.
-
final int
делает его постоянной времени компиляции. Таким образом, -5
уже жестко закодирован в ваш код.
-
Выполняется вызов нового Dog()
и вызывается конструктор, который устанавливает значение 0
+ -5
= -5
.
измените val2
на final
, тогда вы увидите разницу (вы получите -2
в качестве ответа.)
Примечание: статические поля инициализируются как и как они встречаются.
Ответ 4
Последовательность инициализации в вашем тесте;
-
static final int val1 = -5;
//константа как статическая окончательная
-
public static Dog dog = new Dog();
//тогда "собака" инициализируется, но его член val2 не был инициализирован
-
static int val2 = 3;
//наконец, инициализируется 'val2'
Это изменение кода выведет -2
;
public class Dog {
//public static Dog dog = new Dog();
static final int val1 = -5;
static int val2 = 3;
public int val3;
public static Dog dog = new Dog(); //moved here
public Dog() {
val3 = val1 + val2;
}
public static void main(String[] args) {
System.out.println(Dog.dog.val3);
}
}
Ответ 5
Все статические переменные инициализируются в отдельном статическом конструкторе, который выполняется при загрузке классов. В том же порядке, что и в коде. Ваш пример скомпилирован примерно так:
public class Dog {
public static Dog dog;
static final int val1 = -5;
static int val2;
public int val3;
static {
dog = new Dog();
val2 = 3;
}
public Dog() {
val3 = val1 + val2;
}
public static void main(String[] args) {
System.out.println(Dog.dog.val3);
}
}
Вот почему имеет смысл порядок переменных класса/экземпляра. Выполнение конструктора класса происходит в конце инициализации. Константы разрешаются раньше. Для получения дополнительной информации см. Создание экземпляров нового класса.