Странное поведение для 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 (т. е., завернутый вIntegerobject), это не константа времени компиляции и является частью статического блока инициализатора. Однако, если тип былint, количество4будет константой времени компиляции, поэтому ее не нужно будет явно инициализировать. Потому что"abc"является константой времени компиляции, значениеSпечатается как положено.если бы вы заменили,
public static final String S = "abc";С
public static final String S = new String("abc");тогда вы бы заметили выход
Sи"null"как хорошо. Почему это происходит? По той же причине, почемуIвывод"null". Поля, как эти, которые имеют буквальные, значения констант (то не нужна автобоксинг, какString) относятся с"ConstantValue"атрибут при компиляции, что означает, что их значение может быть решено просто заглянув в пул констант класса, без необходимости запускать какой-либо код.