Наследование — важная концепция объектно-ориентированного подхода. Наследование — еще один важный способ улучшить возможность повторного использования кода в дополнение к композиции. В композиции мы увидели, что композиция — это функциональный интерфейс объекта, который вызывается неоднократно. Как мы увидим, наследование позволяет повторно использовать существующие определения классов.
Наследование классов
Когда мы определяли класс раньше, мы начинали с нуля и подробно определяли каждого члена класса. Например, следующий класс Human:
Скопируйте код кода следующим образом:
классЧеловеческий
{
/**
*аксессуар
*/
публичный int getHeight()
{
вернуть this.height;
}
/**
* мутатор
*/
public voidrowHeight(int h)
{
this.height = this.height + h;
}
/**
*дыхание
*/
публичное дыхание пустоты()
{
System.out.println("ху...ху...");
}
частная высота int;
}
Из приведенного выше определения класса мы можем понять все детали класса: члены данных класса, методы класса и интерфейс класса.
Теперь нам нужно определить новый класс, например класс Woman, и предположить, что Woman очень похож на класс Human:
Человек и Женщина
Мы можем начать с нуля и полностью определить класс Woman, как и раньше:
Скопируйте код кода следующим образом:
класс Женщина
{
/**
*аксессуар
*/
публичный int getHeight()
{
вернуть this.height;
}
/**
* мутатор
*/
public voidrowHeight(int h)
{
this.height = this.height + h;
}
/**
*дыхание
*/
публичное дыхание пустоты()
{
System.out.println("ху...ху...");
}
/**
* новый метод
*/
общедоступный человеческий метод GiveBirth()
{
System.out.println("Родить");
возвращение (новый Человек(20));
}
частная высота int;
}
Программисту придется столкнуться с множеством проблем при написании приведенной выше программы. В классе Human написано много определений, но нам придется вводить их заново. Класс Woman добавляет только новый метод GiveBirth() (этот метод создает и возвращает новый объект Human).
Используя наследование, мы можем избежать вышеупомянутого дублирования. Пусть класс Woman наследует от класса Human, и класс Woman автоматически получит функции всех открытых членов класса Human.
Мы используем ключевое слово Extensions для обозначения наследования:
Скопируйте код кода следующим образом:
класс Женщина расширяет права человека
{
/**
* новый метод
*/
общедоступный человеческий метод GiveBirth()
{
System.out.println("Родить");
возвращение (новый Человек(20));
}
}
Таким образом, мы экономим много текста. Посредством наследования мы создаем новый класс, называемый производным классом. Унаследованный класс (Human) называется базовым классом (базовым классом). Производный класс использует базовый класс в качестве основы для собственного определения и дополняет метод GiveBirth(), который не определен в базовом классе. Отношения наследования могут быть выражены как:
Наследование: стрелка указывает на базовый класс.
Для тестирования вы можете использовать следующий класс Test:
Скопируйте код кода следующим образом:
тест публичного класса
{
public static void main(String[] args)
{
Женщина aWoman = новая женщина();
aWoman.growHeight(120);
System.out.println(aWoman.getHeight());
}
}
Производный слой
Путем наследования мы создаем класс Woman. Весь процесс можно разделить на три уровня: определение базового класса, определение производного класса и внешнее использование.
Уровень определения базового класса заключается в обычном определении класса, например, в приведенном выше определении класса Human.
С точки зрения внешних пользователей (например, объекта класса Woman, созданного в классе Test), производный класс имеет унифицированный внешний интерфейс:
Для внешних пользователей вышеуказанного интерфейса достаточно. С точки зрения интерфейса, в производных классах нет ничего особенного.
Однако программисты должны быть осторожны при работе на уровне определений производных классов:
Во-первых, интерфейс смешанный: методы getHeight() и GroveHeight() берутся из базового класса, а метод GiveBirth() определен внутри производного класса.
Есть и другие осложнения. Раньше мы могли свободно получать доступ к членам класса внутри класса (используя это для ссылки на объект). Однако когда мы находимся в области определения класса Woman, мы не можем получить доступ к закрытым членам базового класса Human. Мы помним значение слова Private: частные члены предназначены только для внутреннего использования класса. Класс Женщина — это новый класс, отличный от класса Человек, поэтому он расположен вне класса Человек. В производном классе недоступны частные члены базового класса.
Но самое интересное то, что наши методы GroheHeight() и getHeight() по-прежнему работают. Это показывает, что частные члены базового класса существуют, но мы просто не можем получить к ним прямой доступ.
Чтобы прояснить эту концепцию, нам необходимо понять механизм генерации объектов производного класса. Когда мы создаем объект производного класса, Java фактически сначала создает объект базового класса (подобъект) и добавляет. Другие члены, определенные производным классом, составляют объект производного класса. Внешние пользователи могут видеть открытые члены базового класса и производных классов. Как показано ниже:
Объекты базового класса и объекты производного класса
Желтый цвет на картинке — объект базового класса. Члены базового уровня могут обращаться друг к другу (используйте это в определении класса Human для ссылки на объект базового класса).
Синяя часть — это новое содержимое производного объекта. Я называю эту часть производным слоем. Синяя и желтая части вместе образуют производный объект. Члены производного слоя могут иметь доступ друг к другу (это в определении «Женщина»). Кроме того, мы также можем получить доступ к общедоступным членам базового уровня. По этой причине мы используем ключевое слово super для ссылки на объект базового класса и используем super.member для представления базовых (публичных) членов.
Когда мы находимся на производном уровне (то есть при определении класса Woman), мы не можем получить доступ к закрытым членам красной базы. Когда мы находимся снаружи, мы не можем получить доступ ни к частным членам фиолетового производного слоя, ни к частным членам красного базового слоя.
(Частные элементы производного уровня имеют ограничения доступа, поэтому они отмечены косой чертой. Частные элементы базового уровня имеют наибольшее количество ограничений доступа, поэтому они отмечены косой чертой)
Super аналогичен этому и также является неявным параметром. Когда мы находимся на разных уровнях определения классов, это будет иметь разные значения. Будьте осторожны с ключевыми словами this и super.
(Java не требует принудительного использования this и super. Во многих случаях Java может автоматически определять принадлежность членов. Но я думаю, что это хорошая практика.)
защищенный
Ранее мы представили два ключевых слова, связанных с разрешениями на доступ, Private и Public, которые контролируют внешнюю видимость участников. Теперь мы вводим новое ключевое слово доступа: protected.
Члены, помеченные как защищенные, видимы в этом классе и его производных классах. Эту концепцию легко понять. Другими словами, к защищенным членам базового класса можно получить доступ со стороны производного уровня, но к ним нельзя получить доступ извне, как показано ниже:
переопределение метода
Внешний интерфейс объекта производного класса в конечном итоге состоит из общедоступных членов объекта базового класса и общедоступных членов производного уровня. Если общедоступные члены базового класса и общедоступные члены производного уровня имеют одно и то же имя, какое из них отображается в интерфейсе Java?
В разделе о конструкторах и перегрузке методов мы уже упоминали, что Java использует как имя метода, так и список параметров для определения вызываемого метода. Метод определяется именем метода и списком параметров. В приведенной выше задаче, если только имена методов одинаковы, но списки параметров разные, то два метода будут представлены интерфейсу одновременно, что не доставит нам никаких проблем. При выполнении внешнего вызова Java решает, какой метод использовать (перегрузка метода), на основе предоставленных параметров.
Что, если имя метода и список параметров совпадают? При создании слоя мы также можем использовать super и this, чтобы определить, какой это метод. В случае внешнего мы представляем только унифицированный интерфейс, поэтому мы не можем предоставлять два метода одновременно. В этом случае Java будет отображать методы производного уровня вместо методов базового уровня.
Этот механизм называется переопределением метода. Переопределения методов можно эффективно использовать для изменения методов членов базового класса. Например, на производном уровне, то есть при определении Женщины, вы можете изменить метод дыхания(), предоставляемый базовым классом:
Скопируйте код кода следующим образом:
класс Женщина расширяет права человека
{/**
* новый метод
*/
общедоступный человеческий метод GiveBirth()
{
System.out.println("Родить");
возвращение (новый Человек(20));
}
/**
* переопределить Human.breath()
*/
публичное дыхание пустоты()
{
супер.дыхание();
System.out.println("su...");
}
}
Обратите внимание, что в данный момент мы находимся на производном уровне и все еще можем вызывать метод Breath() объекта базового класса через super. Когда мы вызываем класс Woman извне, из-за переопределения метода мы больше не можем вызывать метод объекта базового класса.
Переопределение метода сохраняет интерфейс объекта базового класса и использует реализацию производного уровня.
Конструктор
После понимания концепций объектов базового класса и производных слоев становится легче понять методы построения производных классов.
Нам нужно определить конструктор с тем же именем, что и класс в определении производного класса. В этом конструкторе:
1. Поскольку при создании производного объекта первым создается и инициализируется объект базового класса, первым должен вызываться конструктор базового класса. Мы можем использовать оператор super(список аргументов) для вызова конструктора базового класса.
2. После создания объекта базового класса начните создавать производный уровень (инициализировать элементы производного слоя). Это то же самое, что и общий метод построения, обратитесь к методу построения и перегрузке метода.
Например, в следующей программе класс Human имеет конструктор:
Скопируйте код кода следующим образом:
классЧеловеческий
{
/**
* конструктор
*/
общественный человек (int h)
{
this.height = ч;
}
/**
*аксессуар
*/
публичный int getHeight()
{
вернуть this.height;
}
/**
* мутатор
*/
public voidrowHeight(int h)
{
this.height = this.height + h;
}
/**
*дыхание
*/
публичное дыхание пустоты()
{
System.out.println("ху...ху...");
}
частная высота int;
}
Определение производного класса Woman class и метод его построения:
Скопируйте код кода следующим образом:
класс Женщина расширяет права человека
{
/**
* конструктор
*/
публичная женщина(int h)
{
супер(h); // конструктор базового класса
System.out.println("Привет, Пандора!");
}
/**
* новый метод
*/
общедоступный человеческий метод GiveBirth()
{
System.out.println("Родить");
возвращение (новый Человек(20));
}
/**
* переопределить Human.breath()
*/
публичное дыхание пустоты()
{
супер.дыхание();
System.out.println("su...");
}
}
Подвести итог
простирается
переопределение метода
защищенный
супер.член, супер()