
- •Основы классов
- •Общая форма класса
- •Простой класс
- •Объявление объектов
- •Операция new
- •Представление методов
- •Добавление метода к классу Box
- •Возврат значений
- •Добавление метода с параметрами
- •Конструкторы
- •Параметризованные конструкторы
- •Ключевое слово this
- •Скрытие переменной экземпляра
- •Сборка "мусора"
- •Метод finalize()
- •Класс Stack
- •Перегрузка конструкторов
- •Использование объектов в качестве параметров
- •Передача аргументов
- •Возврат объектов
- •Рекурсия
- •Управление доступом
- •Статические элементы
- •Спецификатор final
- •Ревизия массивов
- •Вложенные и внутренние классы
- •Класс String
- •Использование аргументов командной строки
- •Наследование
- •Основы наследования
- •11 Тело класса }
- •Доступ к элементам и наследование
- •Практический пример
- •Переменная суперкласса может ссылаться на объект подкласса
- •Использование ключевого слова super
- •Вызов конструктора суперкласса с помощью первой формы super
- •Создание многоуровневой иерархии
- •II построить клон объекта
- •Когда вызываются конструкторы
- •Переопределение методов
- •Динамическая диспетчеризация методов
- •Зачем нужны переопределенные методы?
- •Применение переопределения методов
- •Использование абстрактных классов
- •Void meth() { // ошибка! Нельзя переопределять.
- •Класс Object
Когда вызываются конструкторы
Когда иерархия классов создана, в каком порядке вызываются конструкторы классов, образующих иерархию? Например, если имеется подкласс в и суперкласс а, д-конструктор вызывается перед в-конструкторами, или наоборот? Ответ заключается в том, что в иерархии классов конструкторы вызываются в порядке подчиненности классов – от суперкласса к подклассу. Далее, так как super о должен быть первым оператором, выполняемым в конструкторе подкласса, этот порядок всегда одинаков и не зависит от того, используется ли super (). Если -super о не используется, то сначала будет выполнен конструктор по умолчанию (без параметров) каждого суперкласса. Следующая программа иллюстрирует, когда выполняются конструкторы:
// Демонстрирует порядок вызова конструкторов.
// Создать суперкласс А.
class A { А() {
System.out.println("Внутри А-конструктора.");
}
}
// Создать подкласс В расширением А.
class В extends A {
ВО {
System.out.println("Внутри В-конструктора.");
}
}
// Создать другой класс (С), расширяющий В.
class С extends В {
СО {
System.out.println("Внутри С-конструктора.");
}
}
class CallingCons {
public static void main(String args[]) {
С с = new С();
}
}
Вывод этой программы:
Внутри А-конструктора.
Внутри В-конструктора.
Внутри С-конструктора.
Не трудно видеть, что конструкторы вызываются в порядке подчиненности классов.
Если подумать, то выполнение функций конструкторов в порядке подчиненности классов имеет определенный смысл. Поскольку суперкласс не имеет никакого знания о каком-либо подклассе, любая инициализация, которую ему нужно выполнить, является отдельной и, по возможности, предшествующей любой инициализации, выполняемой подклассом. Поэтому-то она и должна выполняться первой.
Переопределение методов
В иерархии классов, если метод в подклассе имеет такое же имя и сигнатуру типов (type signature), как метод в его суперклассе, говорят, что метод в подклассе переопределяет (override) метод в суперклассе. Когда переопределенный метод вызывается в подклассе, он будет всегда обращаться к версии этого метода, определенной подклассом. Версия метода, определенная суперклассом, будет скрыта. Рассмотрим следующий фрагмент:
// Переопределение методов.
class A {
int i, j;
A(int a, int b) {
i = a;
j = b;
}
// показать i и j на экране
void show() {
System.out.println)"! и j: " + i + " " + j);
}
}
class В extends A {
int k;
В(int a, int b, int c) {
super(a, b);
k = c;
}
// Показать на экране k (этот show() переопределяет show() из А)
void show() {
System.out.println("k: " + k) ;
}
}
class Override {
public static void main(String args[]) {
В subOb = new В (1, 2, 3) ;
subOb.show(); // здесь вызывается show() из В
}
}
Вывод этой программы:
k: 3
Когда show () вызывается для объекта типа в, используется версия show (), определенная в классе в. То есть версия show () внутри в переопределяет (отменяет) версию, объявленную в а.
Если нужно обратиться к версии суперкласса переопределенной функции, то можно сделать это, используя super. Например, в следующей версии подкласс в вызывается show о версия суперкласса а. Это позволяет отобразить все экземплярные переменные.
class В extends A {
int k;
B(int a, int b, int с) {
super(a, b);
k = c;
}
void show() {
super.show(); // вызов show() суперкласса А
System.out.println("k: " + k) ;
}
}
Если вы замените этой версией класс в предыдущей программы, то получите следующий вывод:
i и j : 1 2
k: 3
Здесь super, show о вызывает версию show о суперкласса.
Переопределение метода происходит только тогда, когда имена и сигнатуры типов этих двух методов идентичны. Если это не так, то оба метода просто перегружены. Например, рассмотрим следующую модифицированную версию предыдущего примера:
// Методы с различными сигнатурами типов перегружаются, а не переопределяются,
class A {
int i, j;
A(int a, int b) {
i - a;
j = b; }
// Показать i и j void show(} {
System.out.println("i и j: " + i + " " + j);
}
}
// Создать подкласс В расширением класса А.
class B extends A {
int k;
B(int a, int b, int c) { . •
super(a, b) ;
k = c; }
// Перегруженный sliowf)
void show(String msg) {
System.out.println(msg + k) ;
}
}
class Override {
public static void main(String args[]) {
В subOb = new B(l, 2, 3);
subOb.show("Это k: "); // вызов show() класса В
subOb. show () ; // вызов show () класса А
}
}
Вывод этой программы:
Это k: 3
i и j : 1 2
Версия show о в классе в имеет строчный параметр. Это делает сигнатуру его типов отличной от класса а, который не имеет никаких параметров. Поэтому никакого переопределения (или скрытия имени) нет.