Согласно JLS 4.12.5 :
Каждая переменная в программе должна иметь значение, прежде чем ее значение
используется:
Каждая переменная класса, переменная экземпляра или компонент массива
инициализируется значением по умолчанию при его создании (§15.9, §15.10.2):
Для байта типа значением по умолчанию является ноль, то есть значение
(Байт) 0. * +1010 *
Для типа short значение по умолчанию равно нулю, то есть значение
(Короткий) 0.
Для типа int значение по умолчанию равно нулю, то есть 0.
Для типа long значение по умолчанию равно нулю, то есть 0L.
Для типа float значением по умолчанию является положительный ноль, то есть 0,0f.
Для типа double значением по умолчанию является положительный ноль, то есть 0,0d.
Для типа char значением по умолчанию является нулевой символ, то есть
'\ U0000'.
Для типа boolean значением по умолчанию является false.
Для всех ссылочных типов (§4.3) значение по умолчанию равно нулю.
Каждый параметр метода (§8.4.1) инициализируется соответствующим
значение аргумента, предоставленное инициатором метода (§15.12).
Каждый параметр конструктора (§8.8.1) инициализируется
соответствующее значение аргумента, предоставленное созданием экземпляра класса
выражение (§15.9) или явный вызов конструктора (§8.8.7).
Параметр исключения (§14.20) инициализируется для брошенного объекта
представляющих исключение (§11.3, §14.18).
Локальной переменной (§14.4, §14.14) должно быть явно задано значение
перед его использованием, либо инициализацией (§14.4), либо присвоением
(§15.26), таким образом, который может быть проверен с использованием правил для определенных
назначение (§16 (Определенное назначение)).
Таким образом, если ваше поле нигде не инициализируется перед его использованием, начальное (или значение по умолчанию) будет null
для объектных / ссылочных типов, false
для примитивного boolean
типа или 0
для любых других типов примитивов (0
для char
- это null
символ).
Если поле инициализировано, то нам нужно посмотреть на порядок.
private class A {
protected int a = 2;
public A() {
System.out.println("Printing from constructor of A");
printValues();
System.out.println();
}
public void printValues() {
System.out.println("a = " + a);
}
}
private class B extends A {
private int b = 3;
private int c = initC();
public B() {
super();
System.out.println("Printing from constructor of B");
printValues();
System.out.println();
}
@Override
public void printValues() {
super.printValues(); // Call parent implementation
System.out.println("b = " + b);
System.out.println("c = " + c);
}
private int initC() {
System.out.println("Printing from initC()");
printValues();
System.out.println();
return 4;
}
public static void main(String[] args) {
new B();
}
}
Создает:
Printing from constructor of A
a = 2
b = 0
c = 0
Printing from initC()
a = 2
b = 3
c = 0
Printing from constructor of B
a = 2
b = 3
c = 4
В конструкторе A
(который является родительским классом), a
(который принадлежит A
) уже инициализирован с 2
. Другие 2 поля остаются не инициализированными, возвращая значения, указанные в JLS 4.12.5.
Затем конструктор A
завершает работу и возвращается обратно в конструктор B
(дочерний класс). Вы могли бы ожидать, что он перейдет к части конструктора B
, но перед этим произошло нечто другое - вызывается initC()
. В этот момент мы видим, что b
также был инициализирован, но c
не был инициализирован, поскольку initC()
должен возвращать значение для инициализации c
.
Наконец, мы видим, как инициализируются все 3 поля.
Так что это порядок:
- Большинство полей суперкласса инициализируются первыми.
- При возврате из конструктора суперкласса дочерний класс инициализирует свои собственные поля.
- Конструктор продолжает выполняться, что позволяет использовать инициализированные значения.
Таким образом, инициализация inline в объявлении поля позволяет вам быть уверенным, что поле имеет значение, когда вы используете его, даже в конструкторе, в то время как инициализация в конструкторе может гарантировать только то, что значение инициализируется после , когда оно выходит из конструктора. (дочерний класс может быть уверен, что он тоже инициализирован).