#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