Н.А. Вязовик - Программирование на Java
- Название:Программирование на Java
- Автор:
- Жанр:
- Издательство:неизвестно
- Год:неизвестен
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Н.А. Вязовик - Программирование на Java краткое содержание
Программирование на Java - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Проиллюстрируем этот алгоритм следующим примером:
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);
}
}
Видно, что оба конструктора совершают практически идентичные действия, поэтому можно применить более компактный вид записи:
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) {
this(x2-x1, y2-y1);
}
}
Большим достоинством такого метода записи является то, что удалось избежать дублирования идентичного кода. Например, если процесс инициализации объектов этого класса увеличится на один шаг (скажем, добавится проверка длины на равенство нулю), то такое изменение надо будет внести только в первый конструктор. Такой подход помогает избежать случайных ошибок, так как исчезает необходимость тиражировать изменения в нескольких местах.
Разумеется, такое обращение к конструкторам своего класса не должно приводить к зацикливаниям, иначе будет выдана ошибка компиляции. Цепочка this должна в итоге приводить к super, который должен присутствовать (явно или неявно) хотя бы в одном из конструкторов. После того, как отработают конструкторы всех родительских классов, будет продолжено выполнение каждого конструктора, вовлеченного в процесс создания объекта.
public class Test {
public Test() {
System.out.println("Test()");
}
public Test(int x) {
this();
System.out.println("Test(int x)");
}
}
После выполнения выражения new Test(0) на консоли появится:
Test()
Test(int x)
В заключение рассмотрим применение модификаторов доступа для конструкторов. Может вызвать удивление возможность объявлять конструкторы как private. Ведь они нужны для генерации объектов, а к таким конструкторам ни у кого не будет доступа. Однако в ряде случаев модификатор private может быть полезен. Например:
* private -конструктор может содержать инициализирующие действия, а остальные конструкторы будут использовать его с помощью this, причем прямое обращение к этому конструктору по каким-то причинам нежелательно;
* запрет на создание объектов этого класса, например, невозможно создать экземпляр класса Math ;
* реализация специального шаблона проектирования из ООП Singleton, для работы которого требуется контролировать создание объектов, что невозможно в случае наличия не- private конструкторов.
Инициализаторы
Наконец, последней допустимой конструкцией в теле класса является объявление инициализаторов. Записываются объектные инициализаторы очень просто – внутри фигурных скобок.
public class Test {
private int x, y, z;
// инициализатор объекта {
x=3;
if (x>0)
y=4;
z=Math.max(x, y);
}
}
Инициализаторы не имеют имен, исполняются при создании объектов, не могут быть вызваны явно, не передаются по наследству (хотя, конечно, инициализаторы в родительском классе продолжают исполняться при создании объекта класса-наследника).
Было указано уже три вида инициализирующего кода в классах – конструкторы, инициализаторы переменных, а теперь добавились объектные инициализаторы. Необходимо разобраться, в какой последовательности что выполняется, в том числе при наследовании. При создании экземпляра класса вызванный конструктор выполняется следующим образом:
если первой строкой идет обращение к конструктору родительского класса (явное или добавленное компилятором по умолчанию), то этот конструктор исполняется;
в случае успешного исполнения вызываются все инициализаторы полей и объекта в том порядке, в каком они объявлены в теле класса;
если первой строкой идет обращение к другому конструктору этого же класса, то он вызывается. Повторное выполнение инициализаторов не производится.
Второй пункт имеет ряд важных следствий. Во-первых, из него следует, что в инициализаторах нельзя использовать переменные класса, если их объявление записано позже.
Во-вторых, теперь можно сформулировать наиболее гибкий подход к инициализации final -полей. Главное требование – чтобы такие поля были проинициализированы ровно один раз. Это можно обеспечить в следующих случаях:
Читать дальшеИнтервал:
Закладка: