Наконец, последней допустимой конструкцией в теле класса является объявление инициализаторов. Записываются объектные инициализаторы очень просто — внутри фигурных скобок.
public class Test { private int x, y, z;
// инициализатор объекта
{ x=3; if (x>0) y=4; z=Math.max(x, y); } }
Инициализаторы не имеют имен, исполняются при создании объектов, не могут быть вызваны явно, не передаются по наследству (хотя, конечно, инициализаторы в родительском классе продолжают исполняться при создании объекта класса-наследника).
Было указано уже три вида инициализирующего кода в классах - конструкторы, инициализаторы переменных, а теперь добавились объектные инициализаторы. Необходимо разобраться, в какой последовательности что выполняется, в том числе при наследовании. При создании экземпляра класса вызванный конструктор выполняется следующим образом:
• если первой строкой идет обращение к конструктору родительского класса (явное или добавленное компилятором по умолчанию), то этот конструктор исполняется;
• в случае успешного исполнения вызываются все инициализаторы полей и объекта в том порядке, в каком они объявлены в теле класса;
• если первой строкой идет обращение к другому конструктору этого же класса, то он вызывается. Повторное выполнение инициализаторов не производится.
Второй пункт имеет ряд важных следствий. Во-первых, из него следует, что в инициализаторах нельзя использовать переменные класса, если их объявление записано позже.
Во-вторых, теперь можно сформулировать наиболее гибкий подход к инициализации final-полей. Главное требование — чтобы такие поля были проинициализированы, ровно один раз. Это можно обеспечить в следующих случаях:
• если инициализировать поле при объявлении;
• если инициализировать поле только один раз в инициализаторе объекта (он должен быть записан после объявления поля);
• если инициализировать поле только один раз в каждом конструкторе, в первой строке которого стоит явное или неявное обращение к конструктору родителя. Конструктор, в первой строке которого стоит this, не может и не должен инициализировать final-поле, так как цепочка this-вызовов приведет к конструктору с super, в котором эта инициализация обязательно присутствует.Для иллюстрации порядка исполнения инициализирующих конструкций рассмотрим следующий пример:
public class Test {{
System.out.println("initlallzer");}
intx, y=getY();
final int z;{System.out.println("initializer2");}
private int getY() {System.out.println("getY() "+z);
return z;}
public TestO {System.out.println("Test()");
z=3;}
public Test(int x) {thisO;System.out.println("Test(int)");
// z=4; - нельзя! final-поле уже было инициализировано} }
После выполнения выражения new Test() на консоли появится: