Странное поведение для Java со статическими и окончательный отбор [дубликат]
этот вопрос уже есть ответ здесь:
- порядок инициализации конечных полей 2 ответы
в нашей команде мы нашли какое-то странное поведение, где мы использовали оба static
и final
отбор. Это наш тестовый класс:
public class Test {
public static final Test me = new Test();
public static final Integer I = 4;
public static final String S = "abc";
public Test() {
System.out.println(I);
System.out.println(S);
}
public static Test getInstance() { return me; }
public static void main(String[] args) {
Test.getInstance();
}
}
когда мы запускаем main
метод, мы получаем результат оф:
null
abc
Я бы понял если бы это писал null
значения оба раза, так как код статических членов класса выполняется сверху вниз.
может ли кто-нибудь объяснить, почему такое поведение происходит?
4 ответа:
это шаги, предпринятые при запуске программы:
- до
main
можно запускать, тоTest
класс должен быть инициализирован путем выполнения статических инициализаторов в порядке появления.- инициализации
me
поле, начало выполненияnew Test()
.- выведите значение
I
. Так как тип поляInteger
, что похоже на константу времени компиляции4
становится вычисленным значением (Integer.valueOf(4)
). Инициализатор поля не все же запустите, распечатав начальное значениеnull
.- выведите значение
S
. Поскольку он инициализируется константой времени компиляции, это значение запекается в ссылочный сайт, печатаяabc
.new Test()
завершается, теперь инициализатор дляI
выполняет.урок: если вы полагаетесь на нетерпеливо инициализированные статические синглеты, поместите объявление синглтона как последнее объявление статического поля или прибегните к статическому блоку инициализатора что происходит после всех других статических объявлений. Это сделает класс полностью инициализированным для кода построения синглтона.
S
является константой времени компиляции, следуя правилам JLS 15.28. Так что любое появлениеS
в коде заменяется на значение которого известно во время компиляции.если вы измените тип
I
доint
, вы увидите то же самое для этого тоже.
у вас странное поведение из-за
Integer
тип данных. Относительно JLS 12.4.2 статические поля инициализируются в том порядке, в котором вы их пишете, но сначала инициализируются константы времени компиляции.Если вы не используете тип оболочки
Integer
ноint
типа, вы получите поведение, которое вы хотите.
код
Test
компилируется в:public class Test { public static final Test me; public static final Integer I; public static final String S = "abc"; static { me = new Test(); I = Integer.valueOf(4); } public Test() { System.out.println(I); System.out.println("abc"); } public static Test getInstance() { return me; } public static void main(String[] args) { Test.getInstance(); } }
как видите, конструктор для
Test
вызывается передI
инициализируется. Вот почему он печатает"null"
наI
. Если бы вы поменяли порядок объявления наme
иI
, вы получите ожидаемый результат, потому чтоI
будет инициализирован перед вызовом конструктора. Вы также можете изменить типI
СInteger
доint
., потому что
4
нужно получить autoboxed (т. е., завернутый вInteger
object), это не константа времени компиляции и является частью статического блока инициализатора. Однако, если тип былint
, количество4
будет константой времени компиляции, поэтому ее не нужно будет явно инициализировать. Потому что"abc"
является константой времени компиляции, значениеS
печатается как положено.если бы вы заменили,
public static final String S = "abc";
С
public static final String S = new String("abc");
тогда вы бы заметили выход
S
и"null"
как хорошо. Почему это происходит? По той же причине, почемуI
вывод"null"
. Поля, как эти, которые имеют буквальные, значения констант (то не нужна автобоксинг, какString
) относятся с"ConstantValue"
атрибут при компиляции, что означает, что их значение может быть решено просто заглянув в пул констант класса, без необходимости запускать какой-либо код.