Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Пр _ Часть 1.rtf
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
2.23 Mб
Скачать

Защищенные (protected) методы и переменные.

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

Конструкторы являются специальными методами. Они не являются полноправными объектно-ориентированными членами, поэтому они не наследуются.

Если суперкласс переопределяет метод или переменную суперкласса (другими словами, если подкласс реализует член с таким же именем) - это скрывает член суперкласса. Если быть более точным, переопределение переменной скрывает ее, а переопределение метода просто переопределяет его, но эффект одинаков: переопределенный член суперкласса, по существу, скрыт. Однако вы можете обратиться к членам суперкласса при помощи ключевого слова super:

super.hiddenMemberName

            

В ситуации с классом Adult все, что он пока наследует, - это методы класса Object (например, toString()). То есть, следующий фрагмент кода абсолютно корректен:

Adult anAdult = new Adult(); 

anAdult.toString();

 

Метод toString() не существует явно в Adult. Adult наследует его.

Здесь есть ловушки, о которых вы должны помнить. Во-первых, очень легко дать переменным и методам подкласса такое же имя, которое имеют переменные и методы суперкласса, а затем удивляться, почему вы не можете вызвать наследованный метод. Помните, что давая в подклассе методу то же имя, которое уже существует в суперклассе, вы скрываете его. Во-вторых, конструкторы не наследуются, но они вызываются. Существует неявный вызов конструктора суперкласса в любом конструкторе подкласса, который вы пишите, и это первое действие, которое выполняет конструктор подкласса. Вы должны смириться с этим. Вы никак не можете это изменить. Например, конструктор нашего класса Adult во время выполнения на самом деле выглядит следующим образом, не смотря на то, что мы ничего не вводили в тело конструктора:

public Adult() {

super(); 

}

 

Эта строка в теле конструктора вызывает конструктор суперкласса, не имеющий аргументов. В данном случае это конструктор класса Object.

Определение иерархии классов

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

public class Baby { 

protected int age = 0

protected String firstname = "firstname"; 

protected String lastname = "lastname"; 

protected String gender = "MALE"; 

protected int progress = 0

public Baby() { 

public void move() { 

System.out.println("Moved."); 

public void talk() { 

System.out.println("Spoke."); 

}

Наши классы Adult и Baby выглядят очень похожими. Фактически, они почти идентичны. Такое дублирование кода делает процесс обслуживания кода более болезненным, чем он должен быть. Мы можем создать суперкласс, переместить все общие элементы в этот класс и удалить дублирование кода. Наш суперкласс мог бы называться Person и выглядеть следующим образом:

public class Person { 

protected int age = 0;

protected String firstname = "firstname"; 

protected String lastname = "lastname"; 

protected String gender = "MALE"; 

protected int progress = 0

public Person() { } 

public void move() { 

System.out.println("Moved."); 

public void talk() { 

System.out.println("Spoke."); 

}

Теперь Adult и Baby могут быть подклассами Person, что делает эти два класса очень простыми:

public class Adult extends Person

public Adult() { } 

public class Baby extends Person

public Baby() { } 

}

 

Имея такую иерархию, мы можем обратиться к экземпляру каждого подкласса как к экземпляру любого из его суперклассов в иерархии. Например:

Adult anAdult = new Adult(); 

System.out.println("anAdult is an Object: " + (Adult instanceof Object)); 

System.out.println("anAdult is a Person: " + (Adult instanceof Person)); 

System.out.println("anAdult is anAdult: " + (Adult instanceof Adult)); 

System.out.println("anAdult is a Baby: " + (Adult instanceof Baby));

  Этот код выдает три результата со значением true и один со значением false. Можно также для объекта выполнить приведение типов к любому типу объекта, который находится выше его по иерархии, например:  

Adult anAdult = new Adult(); 

Person aPerson = (Person) anAdult; 

aPerson.move();

 

Ps. нет необходимости указывать явное преобразование из класса в суперкласс. Т.к. в данном случае всё что было в Person есть в Adult, поэтому верно:

Person aPerson = anAdult; 

Этот код откомпилируется без проблем. Мы можем выполнить приведение типа объекта Adult к типу Person, а затем вызвать метод Person с ним.

  Благодаря такой иерархии код наших подклассов упрощается. Вы видите здесь проблему? Теперь все объекты Adult и все объекты Baby будут говорить и перемещаться одинаково. Есть только одна реализация каждого поведения. Это не то, что нам нужно, потому что они должны говорить или двигаться по-разному. Мы могли бы не переопределять методы move() и talk() в подклассах, но тогда мы имели бы, в сущности, бесполезное "стандартное" поведение, определенное в суперклассе. Что нам действительно нужно - это заставить  каждый из наших подклассов передвигаться и говорить своим собственным способом. Именно для этого и существуют абстрактные классы.