Программирование на Java
Шрифт:
Однако логика работы конструкторов имеет и некоторые важные особенности. Поскольку при их вызове осуществляется создание и инициализация объекта, становится понятно, что такой процесс не может происходить без обращения к конструкторам всех родительских классов. Поэтому вводится обязательное правило – первой строкой в конструкторе должно быть обращение к родительскому классу, которое записывается с помощью ключевого слова super.
public class Parent {
private int x, y;
public Parent {
x=y=0;
}
public Parent(int newx, int newy) {
x=newx;
y=newy;
}
}
public class Child extends Parent {
public Child {
super;
}
public Child(int newx, int newy) {
super(newx, newy);
}
}
Как
Проследим мысленно весь алгоритм создания объекта. Он начинается при исполнении выражения с ключевым словом new, за которым следует имя класса, от которого будет порождаться объект, и набор аргументов для его конструктора. По этому набору определяется, какой именно конструктор будет использован, и происходит его вызов. Первая строка его тела содержит вызов родительского конструктора. В свою очередь, первая строка тела конструктора родителя будет содержать вызов к его родителю, и так далее. Восхождение по дереву наследования заканчивается, очевидно, на классе Object, у которого есть единственный конструктор без параметров. Его тело пустое (записывается парой пустых фигурных скобок), однако можно считать, что именно в этот момент JVM порождает объект и далее начинается процесс его инициализации. Выполнение начинает обратный путь вниз по дереву наследования. У самого верхнего родителя, прямого наследника от Object, происходит продолжение исполнения конструктора со второй строки. Когда он будет полностью выполнен, необходимо перейти к следующему родителю, на один уровень наследования вниз, и завершить выполнение его конструктора, и так далее. Наконец, можно будет вернуться к конструктору исходного класса, который был вызван с помощью new, и также продолжить его выполнение со второй строки. По его завершении объект считается полностью созданным, исполнение выражения new будет закончено, а в качестве результата будет возвращена ссылка на порожденный объект.
Проиллюстрируем этот алгоритм следующим примером:
public class GraphicElement {
private int x, y;
// положение на экране
public GraphicElement(int nx, int ny) {
super;
// обращение к конструктору
// родителя Object
System.out.println("GraphicElement");
x=nx;
y=ny;
}
}
public class Square extends GraphicElement {
private int side;
public Square(int x, int y, int nside) {
super(x, y);
System.out.println("Square");
side=nside;
}
}
public class SmallColorSquare extends Square {
private Color color;
public SmallColorSquare(int x, int y, Color c) {
super(x, y, 5);
System.out.println("SmallColorSquare");
color=c;
}
}
После
GraphicElement
Square
SmallColorSquare
Выражение super может стоять только на первой строке конструктора. Часто можно увидеть конструкторы вообще без такого выражения. В этом случае компилятор первой строкой по умолчанию добавляет вызов родительского конструктора без параметров ( super ). Если у родительского класса такого конструктора нет, выражение super обязательно должно быть записано явно (и именно на первой строке), поскольку необходима передача входных параметров.
Напомним, что, во-первых, конструкторы не имеют имени и их нельзя вызвать явно, только через выражение создания объекта. Кроме того, конструкторы не передаются по наследству. То есть, если в родительском классе объявлено пять разных полезных конструкторов и требуется, чтобы класс-наследник имел аналогичный набор, необходимо все их описать заново.
Класс обязательно должен иметь конструктор, иначе невозможно порождать объекты ни от него, ни от его наследников. Поэтому если в классе не объявлен ни один конструктор, компилятор добавляет один по умолчанию. Это public -конструктор без параметров и с телом, описанным парой пустых фигурных скобок. Из этого следует, что такое возможно только для классов, у родителей которых объявлен конструктор без параметров, иначе возникнет ошибка компиляции. Обратите внимание, что если затем в такой класс добавляется конструктор (не важно, с параметрами или без), то конструктор по умолчанию больше не вставляется:
/* Этот класс имеет один конструктор.
*/
public class One {
// Будет создан конструктор по умолчанию
// Родительский класс Object имеет
// конструктор без параметров.
}
/* Этот класс имеет один конструктор. */
public class Two {
// Единственный конструктор класса Two.
// Выражение new Two ошибочно!
public Two(int x) {
}
}
/* Этот класс имеет два конструктора. */
public class Three extends Two {
public Three {
super(1);
// выражение super требуется
}
public Three(int x) {
super(x);
// выражение super требуется
}
}
Если класс имеет более одного конструктора, допускается в первой строке некоторых из них указывать не super, а this – выражение, вызывающее другой конструктор этого же класса.
Рассмотрим следующий пример:
public class Vector {
private int vx, vy;
protected double length;
public Vector(int x, int y) {
super;
vx=x;
vy=y;
length=Math.sqrt(vx*vx+vy*vy);
}
public Vector(int x1, int y1,
int x2, int y2) {
super;
vx=x2-x1;
vy=y2-y1;
length=Math.sqrt(vx*vx+vy*vy);
}
}
Видно, что оба конструктора совершают практически идентичные действия, поэтому можно применить более компактный вид записи: