Главная » Java » Конструкторы Java

0

 

Для достижения целей, выходящих за рамки потребностей простой инициализации, в составе класса предусмотрены специальные члены – конструкторы (constructors). Конструктор – это блок выражений, которые используются для инициализации созданного объекта. Инициализация выполняется до того момента, когда оператор new вернет в вызывающий блок ссылку на объект. Конструкторы обладают тем же именем, что и класс, в составе которого они объявляются. Подобно обычным методам класса, конструкторы способны принимать любое (в том числе и нулевое) число аргументов, но в отличие от методов не могут возвращать значения какого бы то ни было типа. При создании объекта класса, содержащего конструктор, который объявлен с параметрами, оператор new сопровождается наименованием класса и списком соответствующих аргументов, заключенным в круглые скобки. Конструкторы вызываются после присваивания полям вновь созданного объекта значений по умолчанию и выполнения явных инструкций инициализации полей.

 

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

class Body {

public long idNUm;

public String name = "<Без имени>";

             public Body orbits = null;

 

private static long nextID = 0;

 

Body() {

idNUm = nextID++;

}

}

 

Объявление конструктора состоит из имени класса, за которым следуют список (возможно, пустой) параметров, ограниченный круглыми скобками, и тело конструктора – блок выражений, заключенный в фигурные скобки. Конструкторы, как и обычные члены класса, могут быть снабжены теми же модификаторами доступа, но конструкторы, строго говоря, – это члены специального вида; впрочем, подобные тонкие смысловые отличия, как правило, за исключением ситуаций, когда в "игру" вступает наследование.

 

Конструктор класса Body объявлен без параметров. Его назначение довольно важно – он обеспечивает уникальность значения поля idNum вновь создаваемого объекта класса. В исходном варианте класса небольшая ошибка программиста-пользователя, связанная, например, с неаккуратно выполненной операцией присваивания значения полю idNum либо с отсутствием инструкций приращения содержимого поля nextID, могла бы привести к тому, что несколько объектов Body получили бы один и тот же порядковый номер. Подобный результат явно Ошибочен, поскольку нарушает ту часть контракта класса, которая гласит:

"Значения idNum различных объектов класса должны быть уникальными".

 

Передав ответственность за выбор верных значений idNum самому классу, мы раз и навсегда избавимся от подобных ошибок. Теперь конструктор класса Body – это единственный субъект, который изменяет содержимое поля nextID и нуждается в доступе к нему. Поэтому мы можем и должны обозначить переменную nextID модификатором private, чтобы предотвратить возможность обращения к ней за пределами класса. Сделав это, мы исключим один из потенциальных источников ошибок, грозящих будущим пользователям нашего класса.

 

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

 

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

     Body sun = new Body();                 // idNUm = 0

sun. name = "Солнце";

 

     Body earth = new Body();              // idNum = 1

earth.name = "земля";

earth.orbits = sun;

 

В Процессе создания объекта с помощью оператора new конструктор класса Body Вызывается после присваивания полям name и огbits предусмотренных нами Выражений инициализации.

 

Рассмотренный выше случай – когда мы заранее, в момент написания программы осведомлены об имени астрономического объекта и о том, вокруг какого небесного тела проходит орбита его движения, – относительно редок. С этой точки зрения вполне резонным будет создать еще один конструктор, который принимает значения имени объекта и ссылки на центральный объект в качестве аргументов:

Body(String bodyName, Body orbitsAround) {

this();

name = bodyName; orbits = orbitsAround;

}

Как нетрудно заметить, один конструктор класса обращается к другому посредством выражения this – первой исполняемой инструкции в теле конструктора-инициатора. Подобное предложение называют явным вызовом конструктора. Если конструктор, к которому вы намереваетесь обратиться явно, предполагает задание аргументов, при вызове они должны быть переданы. Какой из конструкторов будет вызван – это обусловливается количеством аргументов и набором их типов. В данном случае выражение this означает вызов конструктора без параметров, позволяющего установить значение idNum объекта и увеличить текущее содержимое статического поля nextID на единицу. Обращение к this дает возможность избежать повторения кода инициализации idNum и изменения nextID. Теперь код, предусматривающий создание объектов, становится существенно более простым:

Body sun = new Bоdу("солнце", null);

     Body earth = new Воdу("земля", sun);

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

Если применяется выражение явного вызова конструктора, оно должно быть первой исполняемой инструкцией в теле конструктора-инициатора. Выражения, используемые в качестве аргументов вызова, не должны содержать ссылки на поля и методы текущего объекта – во всех случаях предполагается, что на этой стадии конструирование объекта еще не завершено.

Руководствуясь соображениями логической полноты, мы могли бы ввести в состав класса Body еще один конструктор, предусматривающий задание единственного аргумента и используемый для создания объектов, которые представляют небесные тела, не являющиеся спутниками более крупных светил. К этому конструктору удобно обращаться, когда заранее известно, что значение второго аргумента равно null:

Body(String bodyName) {

this (bodyName, null);

}

Здесь вновь используется прием явного вызова конструктора.

Нередки ситуации, когда в контракте класса содержится требование о том, чтобы код, ответственный за создание объектов этого класса, предоставлял конструкторам класса дополнительную информацию. Например, вы как автор класса Body можете оговорить такое условие: "Все объекты класса Body должны обладать именем". Чтобы гарантировать его выполнение, вы вправе включить в список параметров каждого из конструкторов класса параметр имени – теперь вам не придется заботиться об инициализации поля name.

Ниже перечислено несколько причин, обусловливающих применение специализированных конструкторов.

·      Без помощи конструкторов с параметрами некоторые классы не в состоянии обеспечить свои объекты приемлемыми исходными значениями.

 

·         При использовании дополнительных конструкторов задача определения начальных свойств объектов упрощается (наглядный пример – конструктор класса Body с двумя параметрами).

·         Создание объекта зачастую связано с большими издержками из-за некорректного выбора инициализаторов. Например, если объект класса содержит таблицу и вычислительные затраты на ее конструирование велики, было бы совершенно неразумно предусматривать инициализатор по умолчанию, заведомо зная, что позже так или иначе придется отбросить результат его работы и повторить инициализацию, чтобы придать таблице свойства, нужные в конкретной ситуации. Целесообразнее сразу применить подходящий конструктор, который способен создать таблицу с требуемыми свойствами.

·         Если конструктор не помечен признаком publiс, круг субъектов, которые могут им воспользоваться для создания экземпляров класса, ограничивается. Вы вправе, например, запретить программистам, использующим пакет, создавать объекты класса, предусмотрев для всех конструкторов класса признак доступа на уровне пакета.

Весьма широкое применение находят и конструкторы без параметров. Если, объявляя класс, вы не создали ни одного конструктора какой бы то ни было разновидности, компилятор автоматически включит в состав класса пустой конструктор без параметров. Подобный конструктор (его называют конструктором по умолчанию) создается только в том случае, если других конструкторов не существует, поскольку можно привести примеры классов (таких как, скажем, Attr, рассмотренный в следующей главе), в которых применение конструктора без параметров нецелесообразно или вовсе невозможно. Если необходимы и конструктор без параметров, и один или несколько дополнительных конструкторов, первый должен быть создан явно. Конструктор по умолчанию получает тот же признак доступа, что и класс, для которого он создается, если объявление класса снабжено модификатором publiс, то и конструктор

будет помечен как publiс.

Существует еще одна специальная форма конструктора – конструктор копии, который в качестве аргумента принимает объект текущего типа и создает его копию. Обычно такой конструктор сохраняет в полях нового объекта значения исходного объекта, но подчас семантика класса вынуждает автора предусматривать в теле конструктора копии более изощренные операции. Ниже приведен простой конструктор копии объектов типа Body.

Body(Body other) {

idNum = other.idNum;

name = other.name;

orbits = other.orbits;

}

Подход к решению задачи копирования объектов, связанный с применением специальных конструкторов, в классах стандартных пакетов Java широкого распространения не нашел, поскольку для достижения подобной цели более предпочтительным считается использование метода Сlone ( обратитесь к разделу 3.9 на странице 111). Впрочем, простой конструктор копии предусмотрен, например, в составе класса String, а в классах коллекций (они рассматриваются в главе 16)

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

В тексте конструкторов допускается упоминание объявляемых исключений.

Предложение throws помещается после списка параметров непосредственно Перед открывающей фигурной скобкой, которая отмечает начало тела конструктора. Если в объявлении конструктора присутствует предложение throws, любой метод, который косвенным образом обращается к конструктору при выполнении оператора new, обязан либо обеспечить "отлов" исключений упомянутых типов с помощью соответствующих предложений catch, либо перечислить эти типы в разделе throws собственного объявления.

 

Источник: Арнолд, Кен, Гослинг, Джеймс, Холмс, Дэвид. Язык программирования Java. 3-е изд .. : Пер. с англ. – М. : Издательский дом «Вильяме», 2001. – 624 с. : ил. – Парал. тит. англ.

По теме:

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