Инициализация статических конечных полей в Java

#java #initialization #field #final

#java #инициализация #поле #Финал

Вопрос:

 public class Main {
 static final int alex=getc();
 static final int alex1=Integer.parseInt("10");
 static final int alex2=getc();

public static int getc(){
    return alex1;
}

public static void main(String[] args) {
    final Main m = new Main();
    System.out.println(alex " " alex1  " " alex2);
  } 
}
  

Может кто-нибудь сказать мне, почему это выводит: 0 10 10 ? Я понимаю, что это статическая конечная переменная, и ее значение не должно меняться, но немного сложно понять, как компилятор инициализирует поля.

Ответ №1:

Это проблема упорядочивания. Статические поля инициализируются в том порядке, в котором они встречаются, поэтому, когда вы вызываете getc() для инициализации переменной alex, alex1 еще не был установлен. Сначала вам нужно выполнить инициализацию alex1, тогда вы получите ожидаемый результат.

Комментарии:

1. это был вопрос интервью, на самом деле не проблема, и я пытался понять, почему компилятор присваивает 0 только потому, что поле alex1 еще не было инициализировано ..? потому что alex1 является окончательным, а alex занимает место alex1 =>>alex1 равно 0 ?

2. статические назначения отличаются от переменных-членов и не имеют таких гарантий. каждое статическое присвоение оценивается для того, чтобы загрузчик класса нашел его. итак, в вашем случае сначала он присваивает alex, вызывая getc() . на данный момент alex1 еще не был инициализирован, поэтому возвращается 0.

3. хорошо, спасибо. Еще один вопрос.. если инициализировать в статическом блоке поле alex1 со значением 10, что будут печатать остальные? какое из них выполняется первым? статический блок или инициализация?

4. @Alexx, он все еще инициализируется в том порядке, в котором он отображается. Все, что имеет значение, — это то, где вы размещаете статический блок.

5. опять же, это будет зависеть от порядка. ваше static final int alex1=Integer.parseInt("10"); на самом деле просто сокращение для статического блока, и компилятор превратит его в то же самое.

Ответ №2:

Эта ситуация описана в JLS 8.3.2.3 «Ограничения на использование полей во время инициализации».

Правила JLS допускают использование в вашем вопросе и указывают, что первый вызов getc() вернет значение по умолчанию (неинициализированное) alex .

Однако правила запрещают некоторые виды использования неинициализированных переменных; например

 int i = j   1;
int j = i   1;
  

запрещено.


Ознакомьтесь с некоторыми другими ответами. Это не тот случай, когда компилятор Java «не может разобраться». Компилятор строго реализует то, что указано в спецификации языка Java. (Или, другими словами, можно было бы написать компилятор для обнаружения округлости в вашем примере и вызвать это ошибкой компиляции. Однако, если бы это было сделано, это означало бы отклонение допустимых Java-программ и, следовательно, не было бы соответствующим компилятором Java.)


В комментарии вы заявляете об этом:

… конечные поля всегда должны быть инициализированы при компиляции или во время выполнения перед созданием объекта.

Это неверно.

На самом деле существует два вида final полей:

  • Так называемая «постоянная переменная» действительно вычисляется во время компиляции. (Постоянная переменная — это переменная «примитивного типа или типа String, которая является окончательной и инициализируется постоянным выражением во время компиляции» — см. JLS 4.12.4.). Такое поле всегда будет инициализировано к моменту обращения к нему… по модулю определенных сложностей, которые здесь неуместны.

  • Другие final поля инициализируются в порядке, указанном JLS, и есть возможность увидеть значение поля до его инициализации. Ограничение на final переменные заключается в том, что они должны быть инициализированы один и только один раз во время инициализации класса (для static ) или во время инициализации объекта.


Наконец, этот материал очень похож на поведение «углового случая». Типичному хорошо написанному классу не потребуется обращаться к final полю до его инициализации.

Ответ №3:

Статические конечные поля, значения которых не являются постоянными выражениями во время компиляции, инициализируются в порядке объявления. Таким образом, при alex инициализации, alex1 еще не инициализировано, так что getc() возвращает значения по умолчанию alex1 ( 0 ).

Обратите внимание, что результат будет другим ( 10 10 10 ) в следующем случае:

 static final int alex1 = 10;
  

В этом случае alex1 инициализируется постоянным выражением во время компиляции, поэтому оно инициализируется с самого начала.

Комментарии:

1. 1 Для постоянных выражений во время компиляции. (Зачем еще Integer.parseInt ?)

Ответ №4:

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

например

 public class Main {
    private final int a;

    public Main() {
        System.out.println("Before a=10, a=" getA());
        this.a = 10;
        System.out.println("After a=10, a=" getA());
    }

    public int getA() {
        return a;
    }

    public static void main(String... args) {
        new Main();
    }
}
  

С принтами

 Before a=10, a=0
After a=10, a=10
  

Ответ №5:

Инициализировать переменные класса не обязательно, им автоматически присваиваются значения по умолчанию. Если примитивы (например, int, short …) равны 0 (нулю), то для объектов это null . Следовательно, значение alex1 равно 0. Переменные метода должны быть инициализированы, иначе вы получите ошибку компиляции.

Для лучшего объяснения прочитайте http://download.oracle.com/javase/tutorial/java/javaOO/classvars.html

Комментарии:

1. да, но конечные поля всегда должны быть инициализированы при компиляции или во время выполнения перед созданием объекта. То, что вы сказали, относится к обычным полям, не являющимся конечными ones.ps это работает, и я говорю перед печатью 0 10 10