2016-09-12 5 views
76

В нашей команде мы обнаружили странное поведение, в котором использовались как квалификаторы static, так и final. Это наш тестовый класс:Странное поведение Java со статическими и финальными квалификаторами

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 значение как раз, так как код статического класса элементы выполняются сверху вниз.

Может ли кто-нибудь объяснить, почему это происходит?

+0

Связанный http://stackoverflow.com/questions/4446088/java-in-what-order-are-static-final-fields-initialized (и http://stackoverflow.com/questions/2423376/java-initialization -order-issue-static-vs-instance-fields) – Tunaki

+1

Вы можете посмотреть ответы на [этот вопрос] (http://stackoverflow.com/questions/13431388/final-string-vs-final-integer). надеюсь, что это поможет. –

+0

Хех. Еще одна путаница, связанная с привилегированными типами Java. – imallett

ответ

109

Эти шаги при запуске программы:

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

Занятие: если вы полагаетесь на нетерпеливо инициализированные статические синглтоны, поместите объявление singleton в качестве последнего объявления статического поля или прибегайте к статическому блоку инициализатора, который возникает после всех других статических объявлений. Это приведет к тому, что класс будет полностью инициализирован коду построения singleton.

71

S - это константа времени компиляции, действующая по правилам JLS 15.28. Поэтому любое вхождение S в код заменяется значением, которое известно во время компиляции.

Если вы изменили тип I на int, вы увидите то же самое для этого.

21

У вас странное поведение из-за типа данных Integer. Относительно JLS 12.4.2 статические поля инициализируются в том порядке, в котором вы его записываете, но сначала инициализируются константы компиляции.

Если вы не используете тип обертки Integer, но тип int, вы получите нужное поведение.

13

Ваш 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 необходимо получить автобокс (т. Е. Завернутый в объект Integer), он не является константой времени компиляции и является частью блока статического инициализатора. Однако, если тип был int, то число 4 было бы константой времени компиляции, поэтому его не нужно было бы явно инициализировать. Поскольку "abc" является константой времени компиляции, значение S напечатано, как ожидалось.

Если бы заменить,

public static final String S = "abc"; 

с,

public static final String S = new String("abc"); 

Тогда можно заметить выход S является "null", а также. Почему это происходит? По той же причине, почему I также выдает "null". Такие поля, которые имеют буквальные, постоянные значения (не, требуют автобоксинга, например String) при скомпилировании атрибута "ConstantValue", что означает, что их значение может быть разрешено просто путем поиска в постоянном пуле класса без необходимости для запуска любого кода.

+1

Уверенный об этом? Я бы ожидал 'System.out.println (« abc »);', а не 'System.out.println (S);', поскольку компилятор строит константы времени компиляции. – Tom

+0

Вы правы. В первом случае была бы инструкция 'ldc', а не' getstatic'. Вот что я пытался с последним предложением, я полагаю, я должен прояснить это. –

+0

И ваш код должен показать, что, поскольку вы сказали, что OPs скомпилируется в это. – Tom

 Смежные вопросы

  • Нет связанных вопросов^_^