Наследование в Java


Понятие наследования

Наследование — это краеугольный камень технологии объектно-ориентированного программирования Java, поскольку оно позволяет создавать иерархические классы.

Наследование означает, что подкласс наследует характеристики и поведение родительского класса, так что объекты (экземпляры) подкласса обладают полями экземпляра и методами родительского класса, или подкласс наследует методы от родительского класса, что даёт подклассу такое же поведение, как у родительского класса.

Наследование в повседневной жизни:

Кролики и овцы относятся к классу травоядных, а львы и леопарды — к классу плотоядных.

Травоядные и хищники также принадлежат к классу животных.

Следовательно, отношение, которому должно удовлетворять наследование, — это is-a. Родительский класс является более общим, а подкласс — более конкретным.

Хотя травоядные и хищники оба принадлежат к животным, существуют различия в их атрибутах и поведении. Поэтому подкласс будет обладать общими характеристиками родительского класса, а также своими собственными характеристиками.

Формат наследования классов

В Java можно использовать ключевое слово extends, чтобы объявить, что класс наследуется от другого класса. Общая форма выглядит следующим образом:

Формат наследования классов

classродительский класс{ } classподклассextendsродительский класс{ }

Зачем нужно наследование?

Далее проиллюстрируем эту необходимость на примере.

Разработайте класс животного, где животными являются пингвин и мышь, со следующими требованиями:

  • Пингвин: атрибуты (name, id), методы (eat, sleep, self-introduction)
  • Мышь: атрибуты (name, id), методы (eat, sleep, self-introduction)

Класс Penguin:

public class Penguin { private String name; private int id; public Penguin(String myName, int myid) { name = myName; id = myid; } public void eat(){ System.out.println(name+"Ест"); } public void sleep(){ System.out.println(name+"Спит"); } public void introduction() { System.out.println("Всем привет! Я" + id + "Number" + name + "."); } }

Mouse:

public class Mouse { private String name; private int id; public Mouse(String myName, int myid) { name = myName; id = myid; } public void eat(){ System.out.println(name+"Ест"); } public void sleep(){ System.out.println(name+"Спит"); } public void introduction() { System.out.println("Всем привет! Я" + id + "Number" + name + "."); } }

Из этих двух фрагментов кода видно, что код дублируется, что делает код большим и раздутым, а сопровождаемость низкой (главным образом потому, что при необходимости изменений позже приходится изменять многие части кода, что чревато ошибками). Чтобы кардинально решить проблему этих двух фрагментов кода, необходимо наследование. Выделите общие части этих двух фрагментов кода, чтобы сформировать родительский класс:

Общий родительский класс:

public class Animal { private String name; private int id; public Animal(String myName, int myid) { name = myName; id = myid; } public void eat(){ System.out.println(name+"Ест"); } public void sleep(){ System.out.println(name+"Спит"); } public void introduction() { System.out.println("Всем привет! Я" + id + "Number" + name + "."); } }

Этот класс Animal может служить родительским классом. После того как классы Penguin и Mouse унаследуют этот класс, они получат атрибуты и методы родительского класса. В подклассах больше не будет дублирующегося кода, сопровождаемость повышается, код становится более кратким, а повторное использование кода улучшается (повторное использование в основном означает, что его можно применять многократно, не записывая один и тот же код снова). Код после наследования:

Класс Penguin:

public class Penguin extends Animal { public Penguin(String myName, int myid) { super(myName, myid); } }

Mouse:

public class Mouse extends Animal { public Mouse(String myName, int myid) { super(myName, myid); } }

Типы наследования

Обратите внимание, что Java не поддерживает множественное наследование, но поддерживает многоуровневое наследование.


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

  • Подкласс имеет все атрибуты и методы родительского класса, не являющиеся private.

  • Подкласс может иметь собственные атрибуты и методы, что означает, что подкласс может расширять родительский класс.

  • Подкласс может реализовать методы родительского класса по-своему.

  • Наследование в Java является одиночным, но возможно многоуровневое наследование. Одиночное наследование означает, что подкласс может наследовать только от одного родительского класса. Многоуровневое наследование означает, например, что класс B наследует класс A, а класс C наследует класс B. Таким образом, с точки зрения отношений, класс B является родительским для класса C, а класс A — родительским для класса B. Это особенность, отличающая наследование в Java от наследования в C++.

  • Оно увеличивает связанность между классами (недостаток наследования; высокая связанность делает связи между кодом более тесными, а независимость кода — более слабой).


Ключевые слова наследования

Наследование можно реализовать с помощью двух ключевых слов extends и implements. Кроме того, все классы наследуются от java.lang.Object. Когда класс не использует эти два ключевых слова наследования, он по умолчанию наследует Object (этот класс находится вjava.langпакете, поэтому нет необходимостиimport) класс-предок.

ключевое слово extends

В Java наследование классов является одиночным; то есть подкласс может иметь только один родительский класс, поэтому extends может наследовать только один класс.

ключевое слово extends

public class Animal { private String name; private int id; public Animal(String myName, int myid) { //Инициализировать значения свойств } public void eat() { //Конкретная реализация метода eat } public void sleep() { //Конкретная реализация метода sleep } } public class Penguin extends Animal{ }

ключевое слово implements

Использование ключевого слова implements позволяет косвенно наделить Java характеристиками множественного наследования. Оно применяется, когда класс наследует интерфейсы, и он может наследовать несколько интерфейсов одновременно (интерфейсы разделяются запятыми).

ключевое слово implements

public interface A { public void eat(); public void sleep(); } public interface B { public void show(); } public class C implements A,B { }

ключевые слова super и this

ключевое слово super:Мы можем использовать ключевое слово super для доступа к членам родительского класса; оно используется для ссылки на родительский класс текущего объекта.

ключевое слово this:Ссылка на самого себя, указывающая на текущий объект, т.е. на экземпляр объекта, которому принадлежит текущий метод или конструктор.

Пример

class Animal { void eat() { System.out.println("animal : eat"); } } class Dog extends Animal { void eat() { System.out.println("dog : eat"); } void eatTest() { this.eat(); //this вызывает свой собственный метод super.eat(); //super вызывает метод родительского класса } } public class Test { public static void main(String[] args) { Animal a = new Animal(); a.eat(); Dog d = new Dog(); d.eatTest(); } }

Результат вывода:

animal : eat
dog : eat
animal : eat

ключевое слово final

final может использоваться для модификации переменных (включая атрибуты класса, атрибуты объекта, локальные переменные и формальные параметры), методов (включая методы класса и методы объекта) и классов.

final означает "final".

Использование ключевого слова final для объявления класса определяет класс как final-класс, который не может быть унаследован; или оно используется для модификации методов, и такие методы не могут быть переопределены подклассами:

  • Объявление класса:

    final class 类名 {//类体}
  • Объявление метода:

    修饰符(public/private/default/protected) final 返回值类型 方法名(){//方法体}

Примечание:В классе, определённом с помощью final, его атрибуты и методы не являются final.


Конструктор

Подкласс не наследует конструктор родительского класса (метод конструирования или функцию-конструктор); он лишь вызывает его (неявно или явно). Если конструктор родительского класса имеет параметры, то в конструкторе подкласса вы должны явно использоватьsuper— ключевое слово для вызова конструктора родительского класса с соответствующим списком параметров.

Если конструктор родительского класса не имеет параметров, то в конструкторе подкласса нет необходимости использовать ключевое словоsuperдля вызова конструктора родительского класса; система автоматически вызовет конструктор родительского класса без аргументов.

Пример

class SuperClass { private int n; //Конструктор без аргументов public SuperClass() { System.out.println("SuperClass()"); } //Конструктор с параметрами public SuperClass(int n) { System.out.println("SuperClass(int n)"); this.n = n; } } //Наследование класса SubClass class SubClass extends SuperClass { private int n; //Конструктор без аргументов: автоматически вызывает конструктор родительского класса без аргументов public SubClass() { System.out.println("SubClass()"); } //Конструктор с параметрами: вызывает конструктор родительского класса, имеющий параметры public SubClass(int n) { super(300); System.out.println("SubClass(int n): " + n); this.n = n; } } //наследование класса SubClass2 class SubClass2 extends SuperClass { private int n; //Конструктор без аргументов: вызывает конструктор родительского класса, имеющий параметры public SubClass2() { super(300); System.out.println("SubClass2()"); } //Конструктор с параметрами: автоматически вызывает конструктор родительского класса без аргументов public SubClass2(int n) { System.out.println("SubClass2(int n): " + n); this.n = n; } } public class TestSuperSub { public static void main(String[] args) { System.out.println("------Наследование класса SubClass------"); SubClass sc1 = new SubClass(); SubClass sc2 = new SubClass(100); System.out.println("------Наследование класса SubClass2------"); SubClass2 sc3 = new SubClass2(); SubClass2 sc4 = new SubClass2(200); } }

Результат вывода:

------SubClass 类继承------
SuperClass()
SubClass()
SuperClass(int n)
SubClass(int n): 100
------SubClass2 类继承------
SuperClass(int n)
SubClass2()
SuperClass()
SubClass2(int n): 200
Другие расширения