53

53


При вызове метода toStringO в нем присваивается значение ссылке s4, чтобы все поля были должным образом инициализированы к моменту их использова­ния.

Синтаксис наследования

Наследование является неотъемлемой частью Java (и любого другого языка ООП). Фактически оно всегда используется при создании класса, потому что, даже если класс не объявляется производным от другого класса, он автоматиче­ски становится производным от корневого класса Java Object.

Синтаксис композиции очевиден, но для наследования существует совер­шенно другая форма записи. При использовании наследования вы фактически говорите: «Этот новый класс похож на тот старый класс». В программе этот факт выражается перед фигурной скобкой, открывающей тело класса: сначала записывается ключевое слово extends, а затем имябазового(base)класса.Тем самым вы автоматически получаете доступ ко всем полям и методам базового класса. Пример:

//. reusing/Detergent.java

// Синтаксис наследования и его свойства

import static net mindview util Print.*.

class Cleanser {'

private String s = "Cleanser", public void append(String a) { s += a; } public void diluteO { append( dilutee)"), } public void applyO { appendC applyO"); } public void scrubO { appendC scrubO"): } public String toStringO { return s. } public static void main(String[] args) { Cleanser x = new CleanserO, x diluteO: x applyO, x scrubO; print(x);

}

}

public class Detergent extends Cleanser { II Изменяем метод- public void scrubO {

appendC' Detergent.scrubO").

super scrubO, // Вызываем метод базового класса

}

11 Добавляем новые методы к интерфейсу public void foamO { appendC foamO"), } // Проверяем новый класс, public static void main(String[] args) { Detergent x = new DetergentO, x.diluteO, x.applyO, x scrubO; x. foamO; print(x);

print("Проверяем базовый класс"); Cleanser main(args);

}

} /* Output

Cleanser diluteO applyO Detergent.scrub() scrubO foamO Проверяем базовый класс Cleanser diluteO applyO scrubO */// ~

Пример демонстрирует сразу несколько особенностей наследования. Во-первых, в методе класса Cleanser append() новые строки присоединяются к строке s оператором += — одним из операторов, специально «перегруженных» создателями Java для строк (String).

Во-вторых, как Cleanser, так и Detergent содержат метод main(). Вы можете оп­ределить метод main() в каждом из своих классов; это позволяет встраивать тес­товый код прямо в класс. Метод main() даже не обязательно удалять после за­вершения тестирования, его вполне можно оставить на будущее.

Даже если у вас в программе имеется множество классов, из командной строки исполняется только один (так как метод main() всегда объявляется как public, то неважно, объявлен ли класс, в котором он описан, как public). В нашем примере команда java Detergent вызывает метод Detergent.mainQ. Однако вы так­же можете использовать команду java Cleanser для вызова метода Cleanser.main(), хотя класс Cleanser не объявлен открытым. Даже если класс обладает доступом в пределах класса, открытый метод main() остается доступным.

Здесь метод Detergent.main() вызывает Cleanser.main() явно, передавая ему собственный массив аргументов командной строки (впрочем, для этого годится любой массив строк).

Важно, что все методы класса Cleanser объявлены открытыми. Помните, что при отсутствии спецификатора доступа, член класса автоматически получает доступ «в пределах пакета», что позволяет обращаться к нему только из теку­щего пакета. Таким образом,в пределах данного пакетапри отсутствии специ­фикатора доступа вызов этих методов разрешен кому угодно — например, это легко может сделать класс Detergent. Но если бы какой-то класс из другого па­кета был объявлен производным от класса Cleanser, то он получил бы доступ только к его public-членам. С учетом возможности наследования все поля обыч­но помечаются как private, а все методы — как public. (Производный класс также получает доступ к защищенным (protected) членам базового класса, но об этом позже.) Конечно, иногда вы будете отступать от этих правил, но в любом случае полезно их запомнить.

Класс Cleanser содержит ряд методов: append(), dilute(), apply(), scrub() и toString(). Так как класс Detergentпроизведенот класса Cleanser (с помощью ключевого слова extends), он автоматически получает все эти методы в своем интерфейсе, хотя они и не определяются явно в классе Detergent. Таким обра­зом, наследование обеспечивает повторное использование класса.

Как показано на примере метода scrub(), разработчик может взять уже суще­ствующий метод базового класса и изменить его. Возможно, в этом случае по­требуется вызвать метод базового класса из новой версии этого метода. Однако в методе scrub() вы не можете просто вызвать scrub() — это приведет к рекурсии, а нам нужно не это. Для решения проблемы в Java существует ключевое слово super, которое обозначает «суперкласс», то есть класс, производным от которого является текущий класс. Таким образом, выражение super.scrub() обращается к методу scrub() из базового класса.

При наследовании вы не ограничены использованием методов базового класса. В производный класс можно добавлять новые методы тем же способом, что и раньше, то есть просто определяя их. Метод foam() — наглядный пример такого подхода.

В методе Detergent.main() для объекта класса Detergent вызываются все методы, доступные как из класса Cleanser, так и из класса Detergent (имеется в виду ме­тод foam()).

Инициализация базового класса

Так как в наследовании участвуют два класса, базовый и производный, не сразу понятно, какой же объект получится в результате. Внешне все выглядит так, словно новый класс имеет тот же интерфейс, что и базовый класс, плюс еще не­сколько дополнительных методов и полей. Однако наследование не просто ко­пирует интерфейс базового класса. Когда вы создаете объект производного класса, внутри него содержитсяподобъектбазового класса. Этот подобъект вы­глядит точно так же, как выглядел бы созданный обычным порядком объект ба­зового класса. Поэтому извне представляется, будто бы в объекте производного класса «упакован» объект базового класса.

Report Page