На этом шаге мы рассмотрим конструирование объектов
Ранее было показано, как писать простые конструкторы, определяющие начальные состояния объектов. Но конструирование объектов - очень важная операция, и поэтому в Java предусмотрены самые разные механизмы написания конструкторов.
Напомним, что в классе GregorianCalendar предусмотрено несколько конструкторов. Создать объект этого класса можно следующими способами:
GregorianCalendar today = new GregorianCalendar(); GregorianCalendar deadline = new GregorianCalendar(2099, Calendar.DECEMBER, 31);
Оба способа носят общее название перегрузки. О перегрузке говорят в том случае, если у нескольких методов имеются одинаковые имена, но разные параметры. Компилятор должен сам определить, какой метод вызвать, сравнивая типы параметров, определяемых при объявлении методов, с типами значений, указанных при вызове методов. Если ни один из методов не соответствует вызову или же если одному вызову одновременно соответствует несколько вариантов, возникает ошибка компиляции (этот процесс называется разрешением перегрузки).
Если значение поля в конструкторе явно не задано, ему автоматически присваивается значение по умолчанию: числам - нули, логическим переменным - логическое значение false, а ссылкам на объект - пустое значение null. Но полагаться на действия по умолчанию не следует. Если поля инициализируются неявно, программа становится менее понятной.
Многие классы содержат конструктор без аргументов, создающий объект, состояние которого устанавливается соответствующим образом поумолчанию. Приведем ниже пример конструктора без параметров для класса Employee:
public Employee(){ name = ""; salary = 0; hireDay = new Date(); }
Если в классе совсем не определены конструкторы, автоматически создается конструктор без аргументов. В этом конструкторе всем полям экземпляра присваиваются их значения, предусмотренные по умолчанию. Если же в классе есть хотя бы один конструктор и явно не определен конструктор без параметров, то создавать объекты, не предоставляя аргументы, нельзя.
В определении класса имеется возможность присвоить каждому полю соответствующее значение, как показано ниже:
class Employee { ... private String name = ""; }
Это присваивание выполняется до вызова конструктора.Такой подход оказывается особенно полезным в тех случаях, когда требуется, чтобы поле имело конкретное значение независимо от вызова конструктора класса. При инициализации поля можно использовать не только константу. Приведем ниже пример, в котором поле инициализируется с помощью вызова метода:
class Employee { private static int nextId; private int id = assignId(); ... private static int assignId(){ int r = nextId++; return r; } ... }
Ключевым словом this обозначается неявный параметр метода. Но у этого слова имеется еще одно назначение. Если первый первый оператор конструктора имеет вид this(...), то вызывается другой конструктор этого же класса. Приведем ниже типичный тому пример:
public Employee(double s) { this("Employee " + nextId++, s); }
В Java существует еще один механизм инициализации полей: использование блока инициализаций. Такой блок выполняется каждый раз, когда создается объект данного класса. Расмотрим пример:
class Employee { private static int nextId; private int id; private String name; private double salary; { id = nextId++; } public Employee(){ name = ""; salary = 0; } public Employee(String name, double salary){ this.name = name; this.salary = salary; } ... }
В этом примере начальное значение поля id задается в блоке инициализации объекта, причем неважно, какой именно конструктор испоьзуется для создания экземпляра класса. Блок инициализации выполняется первым, а вслед за ним - тело конструктора. Этот механизм совершенно не обязателен и обычно не применяется.
При таком многообразии способов инициализации полей данных довольно трудно отследить все возможные пути процесса конструирования объектов. Поэтому рассмотрим подробнее те действия, которые происходят при вызове конструктора:
Для инициализации статических полей можно использовать статический блок инициализаций. Приведем пример такого блока:
static { Random generator = new Random(); nextId = generator.nextInt(10000); }
Статическая инициализация выполняется в том случае, если класс загружается впервые. Все операторы, задающие начальные значения статических полей, а также статические блоки инициализации выполняются в порядке их перечисления в объявлении класса.
На следующем шаге мы рассмотрим общие сведения об пакетах