Странное поведение для Java со статическими и окончательный отбор [дубликат]


этот вопрос уже есть ответ здесь:

в нашей команде мы нашли какое-то странное поведение, где мы использовали оба 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 76

4 ответа:

это шаги, предпринятые при запуске программы:

  1. до main можно запускать, то Test класс должен быть инициализирован путем выполнения статических инициализаторов в порядке появления.
  2. инициализации me поле, начало выполнения new Test().
  3. выведите значение I. Так как тип поля Integer, что похоже на константу времени компиляции 4 становится вычисленным значением (Integer.valueOf(4)). Инициализатор поля не все же запустите, распечатав начальное значение null.
  4. выведите значение S. Поскольку он инициализируется константой времени компиляции, это значение запекается в ссылочный сайт, печатая abc.
  5. 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" атрибут при компиляции, что означает, что их значение может быть решено просто заглянув в пул констант класса, без необходимости запускать какой-либо код.