- •ОГЛАВЛЕНИЕ
- •ВВЕДЕНИЕ
- •ГЛАВА 1. ПРИНЦИПЫ ООП. КЛАССЫ И ОБЪЕКТЫ
- •1.1 Основные принципы ООП
- •1.2 Достоинства ООП
- •1.3 Недостатки ООП
- •1.4 Классы и объекты
- •1.5 Члены класса
- •1.6 Модификаторы объявления класса
- •1.7 Пакеты
- •1.8 Понятие имени
- •1.9 Понятие модуля компиляции
- •1.10 Поля
- •1.11 Управление доступом
- •1.12 Создание объектов
- •1.13 Конструкторы
- •1.14 Блоки инициализации
- •1.15 Статическая инициализация
- •1.16 Методы
- •1.16.1 Модификаторы методов
- •1.16.2 Статические методы
- •1.16.3 Вызов метода
- •1.16.4 Выполнение метода и возврат из него
- •1.16.5 Параметры метода
- •1.16.6 Применение методов для управления доступом
- •1.16.7 Ключевое слово this
- •1.16.8 Перегруженные методы
- •1.16.9 Метод main
- •1.16.10 Методы native
- •ГЛАВА 2. ОСНОВЫ ЛЕКСИКИ
- •2.1 Комментарии
- •2.2 Служебные слова
- •2.3 Идентификаторы
- •2.4 Литералы
- •2.4.1 Целочисленные литералы
- •2.4.2 Литералы с плавающей точкой
- •2.4.3 Логические литералы
- •2.4.4 Символьные литералы
- •2.4.5 Строковые литералы
- •2.5 Операторы
- •2.6 Разделители
- •2.7 Переменные
- •2.8 Простые типы
- •2.8.1 Целочисленные типы
- •byte
- •short
- •long
- •2.8.2 Числовые типы с плавающей точкой
- •float
- •double
- •2.8.3 Приведение типа
- •2.8.4 Автоматическое преобразование типов в выражениях
- •2.8.5 Символьный тип
- •2.8.6 Логический тип
- •2.9 Массивы
- •2.9.1 Многомерные массивы
- •2.9.2 Инициализация массивов
- •2.10 Операторы
- •2.10.1 Арифметические операторы
- •Операторы арифметических действий
- •Оператор деления по модулю
- •Операторы арифметических действий с присваиванием
- •Целочисленная арифметика
- •Арифметика с плавающей запятой
- •2.10.2 Инкремент и декремент
- •2.10.3 Целочисленные битовые операторы
- •Операторы битовой арифметики
- •Оператор побитового сдвига влево
- •Операторы побитового сдвига вправо
- •Операторы битовой арифметики с присваиванием
- •2.10.4 Операторы сравнения
- •2.10.5 Логические операторы
- •2.10.6 Оператор проверки соответствия типа
- •2.10.7 Условный тернарный оператор
- •2.10.8 Приоритеты операторов
- •2.11 Управление выполнением метода
- •2.11.1 Завершение работы метода
- •2.11.2 Ветвление
- •2.11.3 Циклы
- •while
- •do-while
- •2.11.4 Прерывание блоков инструкций
- •2.11.5 Переход на следующий виток цикла
- •2.11.6 Блок переключателей
- •ГЛАВА 3. ИСКЛЮЧЕНИЯ
- •3.1 Общие сведения об исключениях
- •3.2 Инструкция throw
- •3.3 Предложения throws
- •3.4 Предложения throws и переопределение методов
- •3.5 Предложения throws и методы native
- •3.6 Блок try-catch-finally
- •ГЛАВА 4. НАСЛЕДОВАНИЕ
- •4.1 Расширенный класс. Конструкторы расширенных классов
- •4.2 Порядок выполнения конструкторов
- •4.3 Переопределение методов при наследовании
- •4.4 Сокрытие полей
- •4.5 Доступ к унаследованным членам класса
- •4.6 Возможность доступа и переопределение
- •4.7 Сокрытие статических членов
- •4.8 Служебное слово super
- •4.9 Совместимость и преобразование типов
- •4.9.1 Совместимость
- •4.9.2 Явное преобразование типов
- •4.10 Проверка типа
- •4.11 Завершённые методы и классы
- •4.12 Абстрактные методы и классы
- •4.13 Класс Object
- •4.13.1 Метод сравнения объектов
- •4.13.2 Метод вычисления хеш-кода
- •4.13.3 Метод клонирования объектов
- •4.13.4 Метод получения ссылки на описание класса
- •4.13.5 Метод завершения работы объекта
- •4.13.6 Метод получения строкового представления
- •ГЛАВА 5. ИНТЕРФЕЙСЫ
- •5.1 Пример простого интерфейса
- •5.2 Объявление интерфейса
- •5.3 Константы в интерфейсах
- •5.4 Методы в интерфейсах
- •5.5 Модификаторы в объявлениях интерфейсов
- •5.6 Расширение интерфейсов
- •5.7 Наследование и сокрытие констант
- •5.8 Наследование, переопределение и перегрузка методов
- •5.9 Пустые интерфейсы
- •5.10 Абстрактный класс или интерфейс?
- •СПИСОК СОКРАЩЕНИЙ
- •СПИСОК ЛИТЕРАТУРЫ
ГЛАВА 4. НАСЛЕДОВАНИЕ
Одно из основных преимуществ ООП состоит в возможности наследования (inheritance), или расширения (extending) функционального потенциала базового (родительского – superclass) класса при построении на его основе производного (дочернего – subclass) класса. Объект нового класса допускается применять в любом контексте, где предусмотрено использование экземпляров исходного класса. Последняя возможность является частным случаем полиморфизма и означает, что объекты определённого класса способны восприниматься как сами по себе, так и в качестве экземпляра любого из исходных классов, от которых наследует действительный класс объекта.
Набор методов и полей класса, открытых для свободного доступа извне, в совокупности с описанием их назначения называют контрактом класса. Контракт – это способ выражения обещаний автора класса относительно того, на что способен и для чего предназначен созданный им продукт. Существуют две формы практиче-
ского воплощения концепции наследования:
•наследование контракта или типа, в результате чего произ-
водный класс получает тип базового и поэтому может быть использован полиморфным образом во всех случаях, где допустимо применение базового класса;
•наследование способов реализации – производный класс при-
обретает реализацию базового в виде набора тел методов.
Очень часто инструменты наследования используются для обеспечения возможности специализации, когда производный класс становится специализированной версией базового класса. В процессе наследования в новом классе может быть предусмотрено, например, изменение способа реализации какого-то метода с целью достижения большей эффективности. При решении задачи расширения класса обе названные выше формы наследования всегда рассматриваются совместно.
133
4.1Расширенный класс. Конструкторы расширенных классов
ВJava в случае наследования классов говорят, что один класс расширяет (extends) другой. Класс может расширить только один класс. В примере 61 класс MyClass2 расширяет (наследует от)
класса MyClass1.
Пример 61. Расширение класса. class MyClass1 {
}
class MyClass2 extends MyClass1{
}
Объект расширенного (производного) класса содержит поля, унаследованные от базового класса, и собственные переменные состояния. Чтобы создать объект расширенного класса, надлежит корректно проинициализировать оба набора переменных. Конструктор производного класса способен обращаться к полям базового, но только базовый класс «осведомлён» о том, как их следует инициализировать, чтобы гарантировать безупречное выполнение контракта класса. Конструкторы расширенного класса обязаны передавать полномочия по инициализации унаследованных полей, явно или косвенно обращаясь к услугам конструкторов базового класса.
Конструктор расширенного класса может напрямую обращаться к конструкторам базового класса посредством ещё одного способа явного вызова конструкторов, предусматривающего применение служебного слова super. Конструкторы расширенных классов могут вызывать друг друга по ключевому слову this().
Если в первой строке тела конструктора производного класса не вызывается ни другой конструктор того же класса, ни конструктор базового класса, компилятор обеспечивает автоматическое обращение к «базовому» конструктору без аргументов – такой вызов осуществляется в первую очередь, прежде всех других
134
исполняемых выражений тела конструктора. Другими словами, первой инструкцией будет super();
Если же в составе базового класса отсутствует конструктор без параметров, в самом начале тела конструктора производного класса обязана явно присутствовать инструкция вызова какоголибо другого конструктора.
Иногда довольно полезной оказывается ситуация, когда конструкторы расширенного класса без параметров или с несколькими параметрами обеспечивают передачу аргументов в конструкторы базового класса. Достаточно общим можно назвать и вариант расширенного класса, в котором нет конструкторов с теми же сигнатурами, что и в базовом.
Конструкторы не относятся к категории методов и не наследуются.
4.2Порядок выполнения конструкторов
Впроцессе создания объекта расширенного класса виртуальная машина выделяет память для хранения всех его полей, включая и те, которые унаследованы от базового класса, и поля получают исходные значения по умолчанию, соответствующие их типам. Далее процесс можно разделить на следующие стадии:
1.вызов конструктора базового класса;
2.присваивание исходных значений полям объекта посредством выполнения соответствующих выражений и блоков инициализации;
3.выполнение инструкций, предусмотренных в теле конструктора.
Впервую очередь выполняется явное или косвенное обращение
кконструктору базового класса. Если осуществляется явный вызов конструктора того же производного класса с помощью ключевого слова this, подобная цепочка вызовов конструкторов этого класса выполняется до тех пор, пока не будет найдена инструкция явного или косвенного обращения к конструктору базового класса, после
135
чего таковой и вызывается. Конструктор базового класса выполняется в соответствии с той же последовательностью стадий – процесс продолжается рекурсивно, завершаясь по достижении конструктора класса Object, поскольку далее не существует конструкторов классов более высокого уровня иерархии. В выражениях, выполняемых в процессе вызова конструктора базового класса, не допускается присутствие ссылок на любые члены текущего объекта.
На второй стадии процесса обрабатываются выражения и блоки инициализации полей в порядке их объявления. В этот момент ссылки на другие члены объекта допустимы, если таковые уже полностью определены.
Наконец, выполняются выражения тела конструктора. Если текущий конструктор был вызван явно, по его завершении управление передаётся в тело конструктора-инициатора, где выполняются оставшиеся инструкции. Процесс повторяется до тех пор, пока управление не будет передано обратно в тело конструктора «исходного» производного класса, название которого было указано в выражении new.
Если во время процесса конструирования выбрасывается исключение, виртуальная машина завершает выполнение оператора new, генерируя то же исключение, и не возвращает ожидаемую ссылку на объект. Поскольку выражение явного вызова конструктора текущего или базового класса должно быть первым в теле конструктораинициатора, отловить исключение, выброшенное вызванным конструктором, невозможно (если бы язык допускал подобное, существовала бы и вероятность созданияобъектов сневернымисходным состоянием).
Пример 24 иллюстрирует различные стадии процесса конструирования объекта производного класса.
Пример 62. Конструктор расширенного класса class X {
protected int xMask = 0x00ff; protected int fullMask;
136
public X() { fullMask = xMask;
}
public int mask (int orig) { return (orig & fullMask);
}
}
class Y extends X {
protected int yMask = 0xff00;
public Y() {
fullMask |= yMask;
}
}
В таблице 17 приведены сведения о содержимом полей, объявленных в классах X и Y, после выполнения каждого этапа процесса создания объекта класса Y.
Таблица 17. Пример инициализации состояния объекта
Этап |
Что происходит |
xMask |
yMask |
fullMask |
0 |
Полям присвоены |
0 |
0 |
0 |
|
значения по умолчанию |
|
|
|
1 |
Вызван конструктор Y |
0 |
0 |
0 |
2 |
Вызван конструктор X |
0 |
0 |
0 |
3 |
Вызван конструктор класса Object |
0 |
0 |
0 |
4 |
Проинициализированы поля X |
0x00ff |
0 |
0 |
5 |
Выполнен конструктор X |
0x00ff |
0 |
0x00ff |
6 |
Проинициализированы поля Y |
0x00ff |
0xff00 |
0x00ff |
7 |
Выполнен конструктор Y |
0x00ff |
0xff00 |
0xffff |
Если в ходе конструирования объекта вызываются методы, важно представлять себе порядок операций и понимать, что происходит на каждом этапе процесса. В подобной ситуации при вызове метода мы всегда имеем дело с версией этого метода для фактического типа объекта; наличие в полях объекта предусмотренных исходных дан-
137