К. Арнольд, Д. Гослинг - Язык программирования Java (1160779), страница 56
Текст из файла (страница 56)
/Инициализация данных подробно рассматривается в разделе "Инициализация", однако в сущности за этимтермином скрывается обычное присвоение начального значения. Если в программе полю не присваивается никакого значения, оно получитзначение ноль, \u0000, false или null, в зависимости от типа./ Однако довольно часто для определения исходного состояния простойинициализации данных оказывается недостаточно; например, могут понадобиться какие-либо исходные данные, или же выполняемые операциине могут быть представлены в виде простого присваивания.Для тех случаев, когда простой инициализации недостаточно, используются конструкторы. Имя конструктора совпадает с именем класса,который он инициализирует.
Конструкторы, подобно методам, могут получать один или несколько параметров, однако они не являютсяметодами и не могут возвращать никакого значения. Параметры конструктора (если они есть) указываются в скобках за именем типа присоздании объекта оператором 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 должны быть разными”.Возлагая ответственность за генерацию значений idNum на сам класс Body, мы тем самым предотвращаем ошибки подобного рода. КонструкторBody становится единственным местом в программе, где idNum присваивается значение. Следующим шагом является объявление поля nextID сключевым словом private, чтобы доступ к нему осуществлялся только из класса. Тем самым мы устраняем еще один возможный источник ошибокдля программистов, работающих с классом Body.Кроме того, такое решение позволит нам свободно изменять способ присвоения значений полю idNums в объектах Body. Например, будущаяреализация нашего класса может просматривать базу данных известных астрономических объектов и присваивать новое значение idNum лишь втом случае, если ранее данному небесному телу не был присвоен другой идентификатор.
Такое изменение никак не скажется на существующемкоде программы, поскольку он никак не участвует в присвоении значения idNum.При инициализации полям name и orbits присваиваются некоторые разумные начальные значения. Следовательно, после приведенного нижевызова конструктора все поля нового объекта Body будут инициализированы. После этого вы можете изменить состояние объекта, присвоив егополям нужные значения:Body sun = new Body();sun.name = “Sol”;// значение idNum равно 0Body earth = new Body();earth.name = “Earth”;earth.orbits = sun;// значение idNum равно 1Конструктор Body вызывается при создании нового объекта оператором new, но после того, как полям name и orbits будут присвоены начальныезначения. Инициализация поля orbits значением null означает, что sun.orbits в нашей программе не присваивается никакого значения.Если создание объекта для небесного тела с двумя параметрами — названием и центром обращения — будет происходить довольно часто, томожно предусмотреть отдельный конструктор, в который оба этих значения передаются в качестве параметров:Body(String bodyName, Body orbitsAround) {this();name = bodyName;orbits = orbitsAround;}Как видите, из одного конструктора можно вызывать другой конструктор этого же класса — для этого первым выполняемым оператором долженбыть вызов this().
Это называется “явным вызовом конструктора”. Если для вызова конструктора необходимы параметры, они могутпередаваться. В нашем случае для присвоения значения полю idNum используется конструктор, вызываемый без аргументов. Теперь созданиеобъектов происходит значительно проще:Body sun = new Body(“Sol”, null);Body earth = new Body(“Earth”, sun);При желании можно задать отдельный конструктор с одним аргументом для тех случаев, когда для создаваемого объекта Body не существуетцентра вращения.
Вызов такого конструктора равносилен применению конструктора с двумя аргументами, при котором второй из них равен null.Для некоторых классов необходимо, чтобы создатель объекта предоставлял некоторые данные. Например, приложение может требовать, чтобыдля всех объектов Body было указано их название. Чтобы убедиться, что всем создаваемым объектам Body передается название, необходимовключить соответствующий параметр во все конструкторы класса Body.Приведем несколько общепринятых соображений в пользу создания специализированных конструкторов:●●●●Некоторые классы не обладают разумным начальным состоянием, если не передать им параметры.При конструировании объектов некоторых видов передача исходного состояния оказывается самым удобным и разумным выходом(примером может служить конструктор Body с двумя аргументами).Конструирование объектов потенциально сопряжено с большими накладными расходами, так что желательно при создании объектасразу устанавливать правильное исходное состояние.
Например, если каждый объект класса содержит таблицу, то конструктор,получающий исходный размер таблицы в качестве параметра, позволит с самого начала создать объект с таблицей нужного размера.Конструктор, атрибут доступа которого отличается от public, ограничивает возможности создания объектов данного класса. Например,вы можете запретить программистам, работающим с вашим пакетом, расширять тот или иной класс, если сделаете все конструкторыдоступными лишь из пакета. Кроме того, можно пометить ключевым словом protected те конструкторы, которые предназначены дляиспользования исключительно в подклассах.Конструкторы, не получающие при вызове никаких аргументов, встречаются настолько часто, что для них даже появился специальный термин:“безаргументные” (no-arg) конструкторы.Если вы не предоставите для класса никаких конструкторов, язык создает безаргументный конструктор по умолчанию, который не делает ничего.Этот конструктор создается автоматически лишь в тех случаях, когда нет никаких других конструкторов, — существуют классы, для которыхбезаргументный конструктор будет работать неверно (например, класс Attr, с которым мы познакомимся в следующей главе).Если безаргументный конструктор должен существовать наряду с одним или несколькими конструкторами, использующими аргументы, можноявно определить его.
Автоматически создаваемый безаргументный конструктор класса, не имеющего суперкласса, эквивалентен следующему(как мы увидим на примере расширенного класса в главе 3):class SimpleClass {/** Эквивалент конструктора по умолчанию */public SimpleClass() {}}Конструктор по умолчанию имеет атрибут public, если такой же атрибут имеет класс, и не имеет его в противном случае.Упражнение 2.6Включите в класс Vehicle два конструктора.
Первый из них — безаргументный, а другой должен получать в качестве аргумента имя владельцамашины. Модифицируйте метод main так, чтобы он выдавал те же результаты, что и раньше.Упражнение 2.7Какие конструкторы вы бы сочли нужным добавить в класс LinkedList?2.6. МетодыМетоды класса обычно содержат код, который анализирует состояние объекта и изменяет его.
Некоторые классы имеют поля public, к которымпрограммисты могут обращаться напрямую, но в большинстве случаев такой подход оказывается не слишком удачным (см. “Проектированиерасширяемого класса”). Многие классы обладают функциями, которые невозможно свести к чтению или изменению некоторой величины — дляних необходимы вычисления.Вызов метода представляет собой операцию, выполняемую с объектом посредством ссылки на него с использованием оператора:reference.method(parameters)Каждый метод вызывается с определенным количеством параметров. Java не поддерживает методов, у которых допускается переменное числопараметров.
Каждый параметр имеет строго определенный тип — примитивный или ссылочный. Кроме того, методы обладают типомвозвращаемого значения, который указывается перед их именем. Например, приведем метод класса Body, который создает строку типа String сописанием конкретного объекта Body:public String toString() {String desc = idNum + “ (” + name + “)”;if (orbits != null)desc += “ orbits ” + orbits.toString();return desc;}В этом методе производится конкатенация объектов String с помощью операторов + и +=. Сначала образуется строка, содержащаяидентификатор и название объекта. Если данное небесное тело обращается вокруг другого, то к ней присоединяется строка с описанием центравращения, для чего вызывается метод toString соответствующего объекта. Последовательность рекурсивных вызовов продолжает строитьцепочку тел, обращающихся вокруг друг друга, пока не будет найдено тело, не имеющее центра вращения.Метод toString не совсем обычен.
Если у объекта имеется метод с именем toString, который вызывается без параметров и возвращает значениетипа String, то он используется для приведения объекта к типу String, если он участвует в конкатенации строк, выполняемой оператором +. Вследующих выражениях:System.out.println(“Body ” + sun);System.out.println(“Body ” + earth);происходит косвенный вызов методов toString для объектов sun и earth, приводящий к следующим результатам:Body 0 (Sol)Body 1 (Earth) orbits 0 (Sol)Существует несколько способов, которыми удается добиться возвращения методом нескольких значений: можно возвращать ссылку на объект, вкотором эти значения хранятся в виде полей; принимать в качестве параметров ссылки на объекты, в которых должны сохраняться результаты;наконец, можно возвращать массив с результатами.К примеру, предположим, что вам нужно написать метод для возвращения перечня финансовых операций, которые определенное лицо можетвыполнять с банковским счетом.
Таких операций может быть несколько (зачисление и снятие средств со счета и т. д.); следовательно, методдолжен возвращать несколько значений. Вот как выглядит объект Permissions, в котором сохраняются логические значения, определяющиедопустимость той или иной операции:class Permissions {public boolean canDeposit,canWithdraw,canClose;}А вот как выглядит метод, заполняющий эти поля:class Account {public Permissions permissionsFor(Person who) {Permissions perm = new Permissions();perm.canDeposit = canDeposit(who);perm.canWithdraw = canWithdraw(who);perm.canClose = canClose(who);return perm;}// ...