Описание и примеры наследования в Java
1. Что такое наследование и ключевое слово extends?
У нас есть следующий класс, описывающий коробку:
public class Box6 {
double width;
double height;
double depth;
Box6(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
public Box6() {
}
double getVolume() {
return width * height * depth;
}
}
И вдруг заказчик решает добавить цветную коробку - ColorBox
и тяжелую коробку - HeavyBox
. Цветная коробка будет отличаться от обычной только цветом, а тяжелая - весом. Получается, что в класс ColorBox
мы должны добавить те же переменные width
, height
и depth
, конструкторы и методы, которые существуют в классе Box
. Дублирование кода в программировании не приветствуется, поэтому для таких случаев придуман такой механизм как наследование.
Используя наследование Java, можно создать класс, который определяет характеристики, общие для набора связанных элементов - Box
. Затем этот общий класс может наследоваться другими, более специализированными классами ColorBox
и HeavyBox
, каждый из которых будет добавлять свои особые характеристики.
В терминологии Java наследуемый класс называется супер классом, а наследующий класс – подклассом. Подкласс наследует все члены, определенные в супер классе, добавляя к ним собственные, особые элементы. Набор классов, связанных отношением наследования, называют иерархией, что изображается таким образом:
Общая форма объявления класса, который наследуется от супер класса:
class имяПодкласса extends имяСуперКласса {
// тело класса
}
Для каждого создаваемого подкласса можно указать только один супер класс. Класс не может стать супер классом для самого себя.
В объявлении класса ColorBox
используется ключевое слово extends для указания того, что ColorBox
является наследником Box6
. ColorBox
содержит все переменные и методы класса Box6
несмотря на то, что в самом классе ColorBox
они не указаны.
public class ColorBox extends Box6 {
String color;
public ColorBox(int width, int height, int depth, String color) {
this.width = width;
this.height = height;
this.depth = depth;
this.color = color;
}
public ColorBox() {
}
}
Один класс может содержать несколько наследников - класс HeavyBox
тоже расширяет Box6
:
public class HeavyBox extends Box6 {
int weight;
public HeavyBox() {
}
public HeavyBox(int width, int height, int depth, int weight) {
this.width = width;
this.height = height;
this.depth = depth;
this.weight = weight;
}
}
В следующем классе создаем объекты всех трех типов и подсчитываем объем для каждой коробки.
public class DifferentBoxExample1 {
public static void main(String[] args) {
Box6 box = new Box6(15, 10, 25);
HeavyBox heavyBox = new HeavyBox(15, 10, 20, 5);
ColorBox colorBox = new ColorBox(25, 12, 20, "красный");
System.out.println("Объём коробки: " + box.getVolume());
System.out.println("Объём коробки: " + heavyBox.getVolume()
+ " Вес коробки: " + heavyBox.weight);
System.out.println("Объём коробки: " + colorBox.getVolume()
+ " Цвет коробки: " + colorBox.color);
}
}
2. Доступ к членам класса и наследование
Несмотря на то, что подкласс включает в себя все члены своего супер класса, он не может иметь доступ к тем членам супер класса, которые объявлены как private
:
public class A {
public int value1;
private int value2;
}
Из класса B
, который является наследником класса A
, невозможно напрямую обратиться к private
переменной класса A
. Доступ к ним можно получить через геттер методы:
public class B extends A {
public int sum() {
// return value1 + value2;
return value1 + getValue2();
}
}
3. Переменная супер класса может ссылаться на объект подкласса
Ссылочной переменной супер класса может быть присвоена ссылка на любой его подкласс.
Например, переменная heavyBox
объявлена как Box
, но она указывает на объект типа HeavyBox
:
Box heavyBox = new HeavyBox(15, 10, 20, 5);
Box colorBox = new ColorBox(25, 12, 20, "красный");
Обратное неверно! Нельзя написать так: HeavyBox heavyBox = new Box(15, 10, 20).
В следующем примере объявлено три переменные типа Box6
, но они указывают на разные объекты.
Для каждого объекта мы можем узнать его ширину, но при попытке обратиться к переменной color
объекта redBox
, возникнет ошибка компиляции. В чем причина такого поведения? Переменная color
объявлена в классе ColorBox
с уровнем доступа по умолчанию, класс DifferentBoxDemo2
находится в том же пакете, то есть переменная color
должна быть доступна. Дело в том, что доступные члены класса определяются типом ссылочной переменной, а не типом объекта, на который она ссылается. То есть если переменная объявлена типа Box6
, нам доступны только члены объявленные в классе Box6
(weight
, height
, depth
), и неважно на какой объект она ссылается. А вот для переменной blueBox
мы можем узнать цвет, так как переменная объявлена как ColorBox
:
public class DifferentBoxExample2 {
public static void main(String[] args) {
Box6 box = new Box6(15, 10, 25);
Box6 heavyBox = new HeavyBox(15, 10, 20, 5);
Box6 redBox = new ColorBox(25, 12, 20, "красный");
ColorBox blueBox = new ColorBox(25, 12, 20, "голубой");
System.out.println("Ширина коробки: " + box.width);
System.out.println("Ширина тяжелой коробки: " + heavyBox.width);
System.out.println("Ширина красной коробки: " + redBox.width);
System.out.println("Ширина голубой коробки: " + blueBox.width);
System.out.println("Цвет голубой коробки: " + blueBox.color);
//System.out.println("Цвет красной коробки: " + redBox.color);
//System.out.println("Вес тяжелой коробки: " + heavyBox.weight);
}
}
4. Создание многоуровневой иерархии
Можно строить иерархии, состоящие из любого количества уровней наследования. Например, добавим класс Shipment
, расширяющий HeavyBox
:
public class Shipment extends HeavyBox1 {
public double cost;
public Shipment(int width, int height, int depth, int weight, double cost) {
super(width, height, depth, weight);
this.cost = cost;
}
public Shipment(double cost) {
this.cost = cost;
}
}
Класс Shipment
содержит не только переменную cost
, объявленную в самом классе, но и переменные класса HeavyBox
и Box6
:
public class DemoShipment {
public static void main(String[] args) {
Shipment shipment = new Shipment(2, 3, 4, 5, 5.3);
System.out.println(shipment.cost);
System.out.println(shipment.depth);
System.out.println(shipment.height);
System.out.println(shipment.weight);
System.out.println(shipment.width);
}
}
5. Порядок вызова конструкторов в многоуровневой иерархии
В иерархии классов конструкторы выполняются в порядке наследования, начиная с супер класса и кончая подклассом. Рассмотрим следующую иерархию классов:
public class E {
public E() {
System.out.println("В конструкторе E");
}
}
public class F extends E {
public F() {
System.out.println("В конструкторе F");
}
}
public class G extends F {
public G() {
System.out.println("В конструкторе G");
}
}
При создании объекта класса G
, сначала закончит свое выполнение конструктор класса E
, потом F
и в конце G
:
public class CallingConstructors {
public static void main(String[] args) {
G g = new G();
}
}
Результат выполнения кода:
В конструкторе E
В конструкторе F
В конструкторе G
Зарегистрируйтесь или войдите, чтобы иметь возможность оставить комментарий.