Главная страница

Java. Полное руководство. 8-е издание. С. Н. Тригуб Перевод с английского и редакция


Скачать 25.04 Mb.
НазваниеС. Н. Тригуб Перевод с английского и редакция
АнкорJava. Полное руководство. 8-е издание.pdf
Дата28.02.2017
Размер25.04 Mb.
Формат файлаpdf
Имя файлаJava. Полное руководство. 8-е издание.pdf
ТипДокументы
#3236
страница15 из 90
1   ...   11   12   13   14   15   16   17   18   ...   90
2 0 Часть I. Язык Вторая форма применения ключевого слова super наиболее подходит в тех ситуациях, когда имена членов подкласса скрывают члены суперкласса с такими же именами. Рассмотрим следующую простую иерархию классов Использование ключевого слова super для предотвращения сокрытия имени
class А {
int i ;
}
// Создание подкласса за счет расширения класса А
class В extends А {
int i;
// эта переменная i скрывает переменную i в классе А
В (int a, int b) {
super.i = а // i в классе А
i = Ь
// i в классе В show() {
System.out.println("i в суперклассе: " + super.i);
System.out.println("i в подклассе " + i);
}
}
class UseSuper {
public static void main(String a r g s []) В subOb = new В (1, 2);
subOb.s h o w (Эта программа отображает следующее в суперклассе:
1
i в подклассе Хотя переменная экземпляра i в классе Вскрывает переменную i в классе А, ключевое слово super позволяет получить доступ к переменной i, определенной в суперклассе. Как вы увидите, ключевое слово super можно использовать также для вызова методов, которые скрываются подклассом.
Создание многоуровневой иерархии
До сих пор мы использовали простые иерархии классов, которые состояли только из суперкласса и подкласса. Однако можно строить иерархии, которые содержат любое количество уровней наследования. Как уже отмечалось, вполне допустимо использовать подкласс в качестве суперкласса другого подкласса. Например, класс Сможет быть подклассом класса В, который, в свою очередь, является подклассом класса А. В подобных ситуациях каждый подкласс наследует все характеристики всех его суперклассов. В приведенном примере класс С наследует все характеристики классов В и А. В качестве примера многоуровневой иерархии рассмотрим следующую программу. В ней подкласс
BoxWeight использован в качестве суперкласса для создания подкласса
Shipment. Класс
Shipment наследует все характеристики классов
BoxWeight и
Box, а также добавляет поле cost, которое содержит стоимость поставки такого пакета Расширение класса BoxWeight за счет включения в него
// стоимости доставки
Глава 8. Наследование 0 5
// Начнем с создания класса Box.
class Box {
private double width;
private double height;
private double depth;
// конструирование клона объекта
Box(Box ob) { // передача объекта конструктору
width = ob.width;
height = ob.height;
depth = ob.depth;
}
// конструктор, используемый при указании всех измерений
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
// конструктор, используемый, когда ни одно из измерений не указано
B o x () {
width = -1;
// значение -1 используется для указания = -1; // неинициализированного
,depth = -1;
// параллелепипеда '
// конструктор, используемый при создании куба
Box(double len) {
width = height = depth = len;
}
// вычисление и возврат объема
double v o l u m e () {
return width * height * depth;
}
}
// Добавление веса BoxWeight extends Box {
double weight; // вес параллелепипеда конструирование клона объекта ob) { // передача объекта конструктору
super(ob);
weight = ob.weight;
}
// конструктор, используемый при указании всех параметров
BoxWeight(double w, double h, double d, double m) {
super(w, h, d ) ; // вызов конструктора суперкласса
weight = m;
}
// конструктор, используемый по умолчанию
BoxWeight() {
super();
weight = -1;
}

2 0 6 Часть I. Язык Java
// конструктор, используемый при создании куба
BoxWeight(double len, double m) {
super(len);
weight = m;
}
// Добавление стоимости доставки
class Shipment extends BoxWeight {
double cost;
// конструирование клона объекта ob) { // передача объекта конструктору
super(ob);
cost = ob.cost;
}
// конструктор, используемый при указании всех параметров
Shipment(double w, double h, double d,
double m, double c) {
super(w, h, d, m ) ; // вызов конструктора суперкласса
cost = с конструктор, используемый по умолчанию
Shipment() {
super();
cost = -1;
}
// конструктор, используемый при создании куба
Shipment(double len, double m, double c) {
super(len, m ) ;
cost = c;
}
class DemoShipment {
public static void main(String a r g s []) {
Shipment shipment1 =
new Shipment(10, 20, 15, 10, 3.41);
Shipment shipment2 =
new Shipment(2, 3, 4, 0.76, 1.28);
double vol;
vol = Объем shipmentl равен " + vol ) ;
System.out.println("Bee shipmentl равен "
+ Стоимость доставки $" + shipmentl.cost)
System.out.println();
vol = Объем shipment2 равен " + vol ) ;
System.out.println("Bee shipment2 равен "
+ Стоимость доставки $" + Вывод этой программы будет следующим
Глава 8. Наследование
2 0 Объем shipment1 равен 3 000.0 Вес shipmentl равен 10.0
Стоимость доставки $3.41
Объем shipment2 равен 24.0 Вес shipment2 равен 0.76 Стоимость доставки Благодаря наследованию, класс
Shipment может использовать ранее определенные классы
Box и
BoxWeight, добавляя только ту дополнительную информацию, которая требуется для его собственного специализированного применения. В этом состоит одно из ценных свойств наследования. Оно позволяет повторно использовать код.
Приведенный пример иллюстрирует важный аспект метод super
() всегда ссылается на конструктор ближайшего суперкласса в иерархии. Метод super () в классе
Shipment вызывает конструктор класса
BoxWeight. Метод super () в классе
BoxWeight вызывает конструктор класса
Box. Если в иерархии классов конструктор суперкласса требует передачи ему параметров, все подклассы должны передавать эти параметры по эстафете. Данное утверждение справедливо независимо оттого, нуждается ли подкласс в собственных параметрах.
На заметку В приведенном примере программы вся иерархия классов, включая
Box,
BoxWeight и
shipment, находится водном файле. Это сделано только ради удобства В Java все три класса могли бы быть помещены в отдельные файлы и компилироваться независимо друг от друга. Фактически использование отдельных файлов — норма, а не исключение при создании иерархий классов.
Порядок вызова конструкторов
В каком порядке вызываются конструкторы классов, образующих иерархию, при ее создании Например, какой конструктор вызывается раньше А (
) или В
( если В
— это подкласса А
— суперкласс? В иерархии классов конструкторы вызываются в порядке наследования, начиная с суперкласса и заканчивая подклассом. Более того, поскольку метод super
() должен быть первым оператором, выполняемым в конструкторе подкласса, этот порядок остается неизменным, независимо оттого, используется ли форма super
(). Если метод super
() не применяется, программа использует конструктор каждого суперкласса, заданный по умолчанию или не содержащий параметров. В следующей программе демонстрируется порядок выполнения конструкторов Демонстрация порядка вызова конструкторов х Создание суперкласса.
class А АО (Внутри конструктора А Создание подкласса за счет расширения класса А
class В extends А ВО (Внутри конструктора В Создание еще одного подкласса за счет расширения класса В

2 0 8 Часть I. Язык Java
class СВ СО Внутри конструктора С CallingCons {
public static void main(String a r g s []) С с = new Эта программа создает следующий вывод.
Внутри конструктора А
Внутри конструктора В Внутри конструктора С
Как видите, конструкторы вызываются в порядке наследования.
Если немного подумать, становится ясно, что выполнение конструкторов в порядке наследования имеет смысл. Поскольку суперкласс ничего не знает о своих подклассах, любая инициализация, которую он должен выполнить, полностью независима и, возможно, обязательна для выполнения любой инициализацией, выполняемой подклассом. Поэтому она должна выполняться первой.
Переопределение методов
Если в иерархии классов имя и сигнатура типа метода подкласса совпадает с атрибутами метода суперкласса, говорят, что метод подкласса переопределяет метод суперкласса. Когда переопределенный метод вызывается из своего подкласса, он всегда будет ссылаться на версию этого метода, определенную подклассом. Версия метода, определенная суперклассом, будет скрыта. Рассмотрим следующий пример Переопределение метода А {

int i ,
j ;
A(int a, int b) {
i = a;
j = b;
}
// отображение i и j
void s h o w O {
System.out.println("i и j: " + i + " " + j);
}
}
class В extends A {
int В (int a, int b, int c) {
super(a, b ) ;
k = с ;
}
// отображение k - этот метод переопределяет метод s h o w O класса А
void show() {
System.out.println("k: " + k ) ;
}
Глава 8. Наследование
2 0 9
class Override {
public static void main(String a r g s []) В subOb = new В (1, 2, 3);
subOb.sho w (); // этот оператор вызывает метод show() класса В
}
\
Эта программа создает следующий вывод.
к: Когда программа вызывает метод show () по отношению к объекту типа Вона использует версию этого метода, определенную внутри класса В. То есть версия метода show ( ), определенная внутри класса В, переопределяет версию, объявленную внутри класса А.
Если нужно получить доступ к версии переопределенного метода, определенного в суперклассе, это можно сделать с помощью ключевого слова s u p e r . Например, в следующей версии класса В версия метода show ( ), объявленная в су­
перклассе, вызывается внутри версии подкласса. Это позволяет отобразить все переменные экземпляров В extends А {

int кВ (int a, int b, int с) { а, Ь ) ;
к = с show () {
super.sho w (); // этот оператор вызывает метод show() класса А
System.out.println("k: " + k ) Подстановка этой версии класса А в предыдущую программу приведет к следующему выводу и j :
1 2 кВ этой версии метод s u p e r . show () вызывает версию метода show ( ), определенную в суперклассе.
Переопределение метода выполняется только в том случае, если имена и сигнатуры типов двух методов идентичны. В противном случае два метода являются просто перегруженными. Например, рассмотрим измененную версию предыдущего примера Методы с различающимися сигнатурами являются
// перегруженными, а не переопределенными
class А {
int i ,
j ;
A(int a, int b) {
i = a;
j = b;
}
// отображение i и j
void show() {
System.out.println("i и j: " + i + " " + j )
;
}
}

2 1 0 Часть I. Язык Java
// Создание подкласса за счет расширения класса А
class В extends А {
int кВ (int a, int b, int с) { а, Ь ) ;
к = с перегрузка метода s h o w ()

void show(String msg) {
System.out.println(msg + k ) ;
}
}
class Override {
public static void main(String a r g s []) В subOb = new В (1, 2 ,
3 )
Это k: "); // вызов метода show() класса В
subOb.s h o w ();
// вызов метода show() класса Эта программа создает следующий вывод.
Это к 3
i и j :
1 Версия метода show ( ), определенная в классе В, принимает строковый параметр. В результате ее сигнатура типа отличается от сигнатуры метода в классе В, который не принимает никаких параметров. Поэтому никакое переопределение или сокрытие имени) не происходит. Вместо этого просто выполняется перегрузка версии метода show ( ), определенной в классе А, версией, определенной в классе В.
Динамическая диспетчеризация методов
Хотя приведенные в предыдущем разделе примеры демонстрируют механизм переопределения методов, они не показывают всех возможностей. Действительно, если бы переопределение методов служило лишь для удобства работы с пространством имен, оно представляло бы только определенный теоретический интерес и имело бы очень небольшое практическое значение. Однако это не так. Переопределение методов служит основой для одной из наиболее мощных концепций Java — динамической диспетчеризации методов Динамическая диспетчеризация методов — механизм, за счет которого осуществляется поиск подходящей версии при обращении к переопределенному методу вовремя выполнения, а не компиляции.
Динамическая диспетчеризация методов важна потому, что именно с ее помощью Java реализует полиморфизм времени выполнения.
Рассмотрение этой концепции начнем с повторной формулировки одного важного принципа ссылочная переменная суперкласса может ссылаться на объект подкласса. Система Java использует этот факт для разрешения обращений к переопределенным методам вовремя выполнения. Вот как это происходит. Когда вызов переопределенного метода реализуется с использованием ссылки на супер­
класс, Java выбирает нужную версию этого метода в зависимости от типа объекта ссылки в момент вызова. Таким образом, этот выбор осуществляется вовремя выполнения. При ссылке на различные типы объектов программа будет обращаться
Глава 8. Наследование
2 1 к различным версиям переопределенного метода. Иначе говоря, выбор для выполнения версии переопределенного метода осуществляется в зависимости от шипа объекта ссылки (а не от типа ссылочной переменной. Следовательно, если суперкласс содержит метод, переопределяемый подклассом, то при наличии ссылки на различные типы объектов через ссылочную переменную суперкласса программа будет выполнять различные версии метода.
В следующем примере иллюстрируется динамическая диспетчеризация методов.
// Динамическая диспетчеризация методов
class А {
void callme() Внутри метода callme класса А В extends А {
// переопределение метода callme()
void callme() Внутри метода callme класса В С extends A {
// переопределение метода callme()
void callme() Внутри метода callme класса С Dispatch {
public static void main(String a r g s []) А а = new А ();
// объект класса А
В b = new В ();
// объект класса В
С с = new С ();
// объект класса С
А г ;
// получение ссылки класса Ага г ссылается на объект А
г .
.callme();
// вызов версии метода определенной в А
г = b;
// г ссылается на объект В
г .
.callme();
// вызов версии метода определенной в В
г = с ;
// г ссылается на объект С
г..callme();
// вызов версии метода определенной в С
}
}
Эта программа создает следующий вывод.
Внутри метода callme класса А Внутри метода callme класса В Внутри метода callme класса С
Эта программа создает один суперкласс Аи два его подкласса В и С. Подклассы В и С переопределяют метод c a ll m e ( ), объявленный в классе А. Внутри метода m ain () программа объявляет объекты классов А, В и С. Программа объявляет также ссылку класса А по имени г. Затем программа по очереди присваивает переменной г ссылку на каждый класс объекта и использует эту ссылку для вызова метода c a l lme (). Как видно из вывода, выполняемая версия метода c a l lme () определяется по классу объекта ссылки вовремя выполнения. Если бы выбор осуществлялся по типу ссылочной переменной, г, вывод отражал бы три обращения к методу callme () класса
А.
На заметку Те читатели, которые знакомы с языками C++ или С, должны заметить, что переопределенные методы в Java подобны виртуальным функциям в этих языках.
Для чего нужны переопределенные методы
Как уже было сказано, переопределенные методы позволяют Java поддерживать полиморфизм времени выполнения. Большое значение полиморфизма для объектно-ориентированного программирования обусловлено следующей причиной он позволяет общему классу указывать методы, которые станут общими для всех его производных классов, в тоже время позволяя подклассам определять конкретные реализации некоторых или всех этих методов. Переопределенные методы — еще один используемый в Java способ реализации аспекта полиморфизма под названием один интерфейс, множество методов”.
Одно из основных условий успешного применения полиморфизма — понимание того, что суперклассы и подклассы образуют иерархию по степени увеличения специализации. В случае его правильного применения суперкласс предоставляет все элементы, которые подкласс может использовать непосредственно. Он определяет также те методы, которые производный класс должен реализовать самостоятельно. Это позволяет подклассу определять собственные методы при сохранении единообразия интерфейса. Таким образом, объединяя наследование и переопределенные методы, суперкласс может определять общую форму методов, которые будут использоваться всеми его подклассами.
Динамический, реализуемый вовремя выполнения полиморфизм — один из наиболее мощных механизмов объектно-ориентированной архитектуры, обеспечивающих повторное использование и надежность кода. Возможность существующих библиотек кода вызывать методы применительно к экземплярам новых классов без повторной компиляции при сохранении четкого абстрактного интерфейса чрезвычайно мощное средство.
Использование переопределения методов
Рассмотрим более реальный пример использования переопределения методов. Следующая программа создает суперкласс
Figure, который хранит размеры двухмерного объекта. Она определяет также метод area ()
, который вычисляет площадь объекта. Программа создает два класса, производных от класса
Figure,

Rectangle и
Triangle. Каждый из этих подклассов переопределяет метод area ()
, чтобы он возвращал соответственно площадь четырехугольника и треугольника Применение полиморфизма времени выполнения
1   ...   11   12   13   14   15   16   17   18   ...   90


написать администратору сайта