15

15


Из результатов программы видно, что ссылка w3 минует двойную инициали¬зацию, перед вызовом конструктора и во время него. (Первый объект теряется, и со временем его уничтожит сборщик мусора.) Поначалу это может показаться неэффективным, но такой подход гарантирует верную инициализацию — что произошло бы, если бы в классе был определен перегруженный конструктор, который не инициализировал бы ссылку w3, а она при этом не получала бы зна¬чения по умолчанию?

Инициализация статических данных

Данные статических полей всегда существуют в единственном экземпляре, не¬зависимо от количества созданных объектов. Ключевое слово static не может применяться к локальным-переменным, только к полям. Если статическое поле относится к примитивному типу, при отсутствии явной инициализации ему присваивается значение по умолчанию. Если это ссылка на объект, то ей при¬сваивается значение null.

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

Следующий пример помогает понять, когда инициализируется статическая память:

//• initialization/StaticInitialization.java // Указание значений по умолчанию в определении класса, import static net mindview util Print *;

class Bowl {

Bowl(int marker) {

print("Bowl(" + marker + ")"),

}

void fl(int marker) {

print("fl(" + marker + ")");

}

}

class Table {

static Bowl bowll = new Bowl(l); TableO {

print("Table()"); bowl 2.f1(1),

}

void f2(iлt marker) {

print("f2(" + marker + ")");

}

static Bowl bowl2 = new Bowl(2);

}

class Cupboard {

Bowl bowl3 = new Bowl(3), static Bowl bowl4 = new Bowl(4). CupboardO {

printC'CupboardO"). bowl4 fl(2).

}

void f3(int marker) {

print(Mf3(" + marker + ")"),

}

static Bowl bowl5 = new Bowl(5);

}

public class Staticlnitialization {

public static void main(String[] args) {

print ("Создание нового объекта Cupboard в mainO"), new CupboardO;

print ("Создание нового объекта Cupboard в mainO"), new CupboardO; table.f2(l). cupboard f3(l),

}

static Table table = new TableO, static Cupboard cupboard = new CupboardO; } /* Output; Bowl(1) Bowl(2) TableO fl(l) Bowl(4) Bowl(5) Bowl(3) Cupboard() fl(2)

Создание нового объекта Cupboard в mainO Bowl(3) CupboardO fl(2)

Создание нового объекта Cupboard в mainO Bowl(3)

CupboardО fl(2) f2(l) f3(l) *///:-

Класс Bowl позволяет проследить за процессом создания классов; классы Table и Cupboard содержат определения статических объектов Bowl. Заметьте, что в классе Cupboard создается нестатическая переменная Bowl bowl3, хотя все ос¬тальные определения — статические.

Из выходных данных программы видно, что статическая инициализация происходит только в случае необходимости. Если вы не создаете объектов Table и никогда не обращаетесь к Table.bowll или Table.bowl2, то, соответственно, не будет и объектов static Bowl bowll и static Bowl bowl2. Они инициализируются только при создании первого объекта Table (или при первом обращении к стати¬ческим данным). После этого статические объекты повторно не переопределя¬ются.

Сначала инициализируются static-члены, если они еще не были проинициали- зированы, и только затем нестатические объекты. Доказательство справедливости этого утверждения легко найти в результате работы программы. Для выполне¬ния main() (а это статический метод!) загружается класс Staticlnitialization; за¬тем инициализируются статические *поля table и cupboard, вследствие чего за¬гружаются эти классы. И так как все они содержат статические объекты Bowl, загружается класс Bowl. Таким образом, все классы программы загружаются до начала main(). Впрочем, эта ситуация нетипична, поскольку в рядовой про¬грамме не все поля объявляются как статические, как в данном примере.

Неплохо теперь обобщить знания о процессе создания объекта. Для примера возьмем класс с именем Dog:

• Хотя ключевое слово static и не используется явно, конструктор в дейст¬вительности является статическим методом. При создании первого объ¬екта типа Dog или при первом вызове статического метода-обращения к статическому полю класса Dog, интерпретатор Java должен найти класс Dog.class. Поиск осуществляется в стандартных каталогах, перечислен¬ных в переменной окружения С LASS PATH.

• После загрузки файла Dog.class (с созданием особого объекта Class, о кото¬ром мы узнаем позже) производится инициализация статических элемен¬тов. Таким образом, инициализация статических членов проводится только один раз, при первой загрузке объекта Class.

• При создании нового объекта конструкцией new Dog() для начала выделя¬ется блок памяти, достаточный для хранения объекта Dog в куче.

• Выделенная память заполняется нулями, при этом все примитивные поля объекта Dog автоматически инициализируются значениями по умолчанию (ноль для чисел, его эквиваленты для типов boolean и char, null для ссылок).

• Выполняются все действия по инициализации, происходящие в точке оп¬ределения полей класса.

• Выполняются конструкторы. Как вы узнаете из главы 7, на этом этапе выполняется довольно большая часть работы, особенно при использова¬нии наследования.

Явная инициализация статических членов

Язык Java позволяет сгруппировать несколько действий по инициализации объектов static в специальной конструкции, называемой статическим блоком. Выглядит это примерно так:

// initialization/Spoon.java public class Spoon { static int i, static {

i = 47,

}

} ///:-

Похоже на определение метода, но на самом деле мы видим лишь ключевое слово static с последующим блоком кода. Этот код, как и остальная инициали¬зация static, выполняется только один раз: при первом создании объекта этого класса или при первом обращении к статическим членам этого класса (даже если объект класса никогда не создается). Например:

II- initialization/ExplicitStatic java

II Явная инициализация с использованием конструкции "static"

import static net.mindview util.Print *;

class Cup {

CupCint marker) {

print("Cup(" + marker + ")"),

}

void f(int marker) {

printC'fC + marker + ")"),

}

}

class Cups {

static Cup cupl, static Cup cup2; static {

cupl = new Cup(l); cup2 = new Cup(2);