Главная страница
Навигация по странице:

  • Public

  • Приложение. Методы класса Object. Сборщик мусора.

  • Лабораторная работа 2. Классы и объекты в языке Java. Наследование


    Скачать 110.5 Kb.
    НазваниеЛабораторная работа 2. Классы и объекты в языке Java. Наследование
    Анкорjava2.doc
    Дата19.03.2019
    Размер110.5 Kb.
    Формат файлаdoc
    Имя файлаjava2.doc
    ТипЛабораторная работа
    #26073

    Лабораторная работа №2.

    Классы и объекты в языке Java. Наследование.

    Цель работы: 1) научиться создавать собственные классы в языке Java. 2) освоить на практике основные принципы ООП.

    Продолжительность работы: 4 часа.

    Теоретические сведения


    Как объектно-ориентированный язык программирования, Java располагает средствами построения классов и объектов. Класс – основное понятие в ООП, он определяет шаблон, форму, по которому создаются объекты. Каждый объект в Java имеет тип. Типом является класс, к которому принадлежит данный объект. В каждом классе есть члены двух видов: поля (переменные, содержащие данные класса и его объектов) и методы (содержащие исполняемый код класса).

    Объекты создаются посредством выражений, в которых используется ключевое слово new. Созданные на основе определения класса объекты – это экземпляры (instances) данного класса. Объекты размещаются в области системной памяти – куче. Доступ к любому объекту осуществляется с помощью ссылки на объект (вместо самого объекта в переменных содержится лишь ссылка на него). Каждый новый объект класса обладает собственной копией его полей, и поля объектов называют переменными экземпляра (instance variables).

    Простейшее описание класса в языке Java имеет вид:

    class class_name

    {

    // описание класса

    }
    Код класса всегда может обращаться ко всем полям и методам данного класса. Для управления доступом к ним из других классов, а также для управления наследованием их в подклассах члены классов могут объявляться с одним из четырех атрибутов доступа:

    • Открытый(Public): к членам класса всегда можно обращаться из любого места, в котором доступен сам класс; такие члены наследуются в подклассах.

    • Закрытый (Private): доступ к членам класса осуществляется только из самого класса.

    • Защищенный (Protected): к данным членам разрешается доступ из подклассов и из функций, входящих в тот же пакет. Такие члены наследуются подклассами.

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

    В итоге, контроль доступа к данным и методам объекта в Java несколько отличается от С++, т.к. помимо трех уровней доступа, имеющихся в С++ имеется четвертый, находящийся где-то между уровнями public и protected. Он не имеет имени и используется по умолчанию, когда явно не указан другой уровень. Поля этого типа доступны внутри только одного программного пакета. В Java контроль доступа реален, поскольку он осуществляется не только при компиляции, но и непосредственно перед запуском кодов на выполнение виртуальной машиной.

    Пример простейшего работающего класса, представляющего точку на плоскости:

    class Point

    {

    public double x, y;

    }
    Чтобы использовать поле для хранения информации, относящейся ко всему классу, следует объявить его с ключевым словом static, такие поля называют статическими.

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

    Класс также может содержать блоки статической инициализации, которые присваивают значения статическим полям или выполняют иную необходимую работу. Статический инициализатор оказывается наиболее полезным в тех случаях, когда простой инициализации в объявлении поля недостаточно. Например, создание статического массива часто должно выполняться одновременно с его инициализацией в операторах программы. Приведем пример инициализации небольшого массива с простыми числами:

    class Numbers

    {

    protected static int[] knownNumbers = new int[4];
    static

    {

    knownNumbers [0] = 2;

    for(int i = 1; i < knownNumbers.length; i++)

    knownNumbers [i] = nextNumbers();

    }

    }
    В языке Java, как объектно-ориентированном, операции над классом осуществляются с помощью методов класса, в которых часто используются детали реализации класса, скрытые от остальных объектов. В таком процессе сокрытия данных, и их недоступности для всех прочих объектов заключается основной смысл инкапсуляции данных.

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

    Итак, в хорошо спроектированном классе данные обычно скрываются, чтобы они могли изменяться только методами этого класса. Чтобы вызвать метод, необходимо указать имя объекта и имя метода и разделить их точкой. Параметры передаются методу в виде заключенного в скобки списка значений, разделяемых запятыми. Если метод вызывается без параметров, необходимо указать пустые скобки.

    В качестве результата работы метода может возвращаться только одно значение. Чтобы метод возвращал несколько значений, следует создать специальный объект, единственное назначение которого — хранение возвращаемых значений, и вернуть этот объект.

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

    Как было сказано выше, в классе могут присутствовать статические поля, относящиеся к классу в целом, а не к его конкретным экземплярам. По аналогии с ними могут существовать и статические методы, также относящиеся ко всему классу, которые часто называют методами класса. Статические методы обычно предназначаются для выполнения операций, специфичных для данного класса, и работают со статическими полями, а не с конкретными экземплярами класса. Для объявления таких методов также используется ключевое слово static.

    Методы могут быть объявлены статическими, так как они работают не с каким-то определенным объектом, но составляют внутри класса группу со сходными функциями.

    Статический метод не может напрямую обращаться к нестатическим членам. При вызове статического метода не существует ссылки на конкретный объект, для которого вызывается данный метод. Это ограничение можно обойти, передавая ссылку на конкретный объект в качестве параметра статического метода. Тем не менее, в общем случае статические методы выполняют свои функции на уровне всего класса, а нестатические методы работают с конкретными объектами. Статический метод, модифицирующий поля объектов, — примерно то же самое, что фабрика по производству определенного товара, пытающаяся изменить серийный номер давно проданного экземпляра.

    Рассмотрим пример:

    public class Point

    {

    private static String destinaton;

    static

    {

    destinaton = "Плоскость";

    }

    private double x = 0;

    private double y;

    // Конструктор по умолчанию (без параметров)

    public Point()

    {

    y = 5;

    }

    // Переопределенный конструктор (overloading), принимающий 2 параметра x и y

    public Point(double x, double y)

    {

    this.x = x;

    this.y = y;

    }

    // реализуем 2 метода для получения и установки значения переменной X (реализуем инкапсуляцию)

    public double GetX()

    {

    return x;

    }

    public void SetX(double x)

    {

    this.x = x;

    }

    public double GetY()

    {

    return y;

    }

    public void SetY(double y)

    {

    this.y = y;

    }

    // 2 статических метода для получения и установки значения статической переменной

    public static String GetDestination()

    {

    return destinaton;

    }

    public static void SetDestination(String _destination)

    {

    destinaton = _destination;

    }

    }

    public class Example

    {

    public static void main(String[] args)

    {

    // создадим новый объект класса, используя конструктор по умолчанию и выведем на экран

    // значения переменных

    Point p1 = new Point();

    System.out.println(p1.GetX());

    System.out.println(p1.GetY());

    System.out.println(p1.GetDestination());

    System.out.println();

    // изменим значение переменной x, используя метод SetX и выведем обновленные значения на

    // экран

    p1.SetX(0.5);

    System.out.println(p1.GetX());

    System.out.println(p1.GetY());

    System.out.println(p1.GetDestination());

    System.out.println();

    // создадим новую переменную типа Point, используя конструктор с параметрами

    Point p2 = new Point(10, 10);

    System.out.println(p2.GetX());

    System.out.println(p2.GetY());

    System.out.println(p2.GetDestination());

    System.out.println();

    // изменим значение статической переменной

    p2.SetDestination("сфера");

    // выведем значения статического поля для каждой из переменных (будут одинаковы)

    System.out.println(p1.GetDestination());

    System.out.println(p2.GetDestination());

    }

    }
    Полиморфизм означает возможность применения одноименных методов с одинаковыми или различными наборами параметров в одном классе или в группе классов, связанных отношением наследования. Понятие полиморфизма, в свою очередь, опирается на два других понятия: совместное использование overloading (перегрузку, доопределение, совместное использование) и переопределение overriding.

    В языке Java каждый метод обладает определенной сигнатурой, которая представляет собой совокупность имени с количеством и типом параметров. Два метода могут иметь одинаковые имена, если их сигнатуры отличаются по количеству или типам параметров. Это называется перегрузкой (overloading), поскольку простое имя метода “перегружается” несколькими значениями. Когда программист вызывает метод, компилятор по количеству и типу параметров ищет тот из существующих методов, сигнатура которого подходит лучше всех остальных. Тип возвращаемого значения не является определяющим фактором при совместном использовании — при вызове метода транслятору нужно определить, какой из одноименных методов вызывать, а тип возвращаемого значения, в общем случае, не позволяет сделать это однозначно. Поэтому нельзя описать в рамках одного класса два метода с одинаковым набором параметров и разными типами возвращаемых значений. Это статический полиморфизм методов классов.

    В Java (как и в других объектно-ориентрованных языках) выполняется вызов метода данного объекта с учетом того, что объект может быть не того же класса, что и ссылка, указывающая на него. Т.е. выполняется вызов метода того класса, к которому реально относится объект. Это динамический полиморфизм методов. Он называется поздним связыванием (dynamic binding, late binding, run-time binding). В C++ соответствующий механизм называется механизмом виртуальных функций.

    Исключительно полезную концепцию объектно-ориентированного программирования представляют собой абстрактные классы. С их помощью можно объявлять классы, реализованные лишь частично, поручив полную реализацию расширенным классам.

    Абстракция оказывается полезной, когда некоторое поведение характерно для большинства или всех объектов данного класса, но некоторые аспекты имеют смысл лишь для ограниченного круга объектов, не составляющих суперкласса. В Java такие классы объявляются с ключевым словом abstract, и каждый метод, не реализованный в классе, также объявляется abstract.

    Если все, что вам требуется, — это определить набор методов, которые будут где-то поддерживаться, но не предоставлять для них реализации, то вместо абстрактных классов лучше воспользоваться интерфейсами.

    Одним из самых больших достоинств объектно-ориентированного программирования является возможность такого расширения, или создания подкласса, существующего класса, при котором можно использовать код, написанный для исходного класса.

    При расширении класса на его основе создается новый класс, наследующий все поля и методы расширяемого класса. Исходный класс, для которого проводилось расширение, называется суперклассом.

    Если подкласс не переопределяет (override)поведение суперкласса, то он наследует все свойства суперкласса. Переопределение же позволяет расширенному классу по-новому реализовать один или несколько унаследованных методов.

    Если имя класса родителя не указано, считается, что родителем является класс Object.

    В то время как this ссылается на члены текущего объекта, ссылка super используется для ссылок на члены суперкласса. При вызове метода super.method() runtime - система просматривает иерархию классов до первого суперкласса, содержащего method(). Во всех остальных ссылках при вызове метода используется тип объекта, а не тип ссылки на объект.

    Все действия по инициализации объектов при наследования классов выполняются этап за этапом в порядке наследования классов.

    • При первом обращении к классу выделяется память под статические поля класса и выполняется их инициализация.

    • Выполняется распределение памяти под создаваемый объект.

    • Выполняются все инициализаторы нестатических полей класса.

    • Выполняется вызов конструктора класса.

    При расширении класса необходимо выбрать один из конструкторов суперкласса и вызывать его при конструировании объектов нового класса. Это необходимо для правильного создания части объекта, относящейся к суперклассу, помимо установки правильного исходного состояния для всех добавленных полей.

    Конструктор суперкласса может вызываться в конструкторе подкласса посредством явного вызова super(). Если вызов конструктора суперкласса не является самым первым выполняемым оператором в конструкторе нового класса, то перед выполнением последнего автоматически вызывается безаргументный конструктор суперкласса. Если же суперкласс не имеет безаргументного конструктора, вы должны явно вызвать конструктор суперкласса с параметрами. Вызов super() непременно должен быть первым оператором нового конструктора.

    Как говорилось ранее, классы, для которых не указан расширяемый класс, являются неявным расширением класса Object. Все ссылки на объекты полиморфно относятся к классу Object, который является базовым классом для всех ссылок, которые могут относиться к объектам любого класса:

    Object student = new Child(); // Object = Child

    student = “oxford student”; // Object = String
    Объекту student вполне законно присваиваются ссылки на объекты Child и String, несмотря на то, что эти классы не имеют между собой ничего общего, за исключением неявного суперкласса Object.
    Рассмотрим пример:

    // нельзя создать переменную абстрактного класса

    public abstract class Human

    {

    private String name;

    private int age;

    private String sex;

    public String GetName()

    {

    return name;

    }

    public int GetAge()

    {

    return age;

    }

    public String GetSex()

    {

    return sex;

    }

    public void SetName(String name)

    {

    this.name = name;

    }

    public void SetAge(int age)

    {

    this.age = age;

    }

    public void SetSex(String sex)

    {

    this.sex = sex;

    }

    protected Human(String name, int age, String sex)

    {

    this.name = name;

    this.age = age;

    this.sex = sex;

    }

    // переопределим метод toString

    public String toString()

    {

    return name + "\t" + age + "\t" + sex;

    }

    }

    // класс студент расширяет класс Human

    public class Student extends Human

    {

    private String patronymic;

    public String GetPatronymic()

    {

    return patronymic;

    }

    public void SettPatronymic(String patronymic)

    {

    this.patronymic = patronymic;

    }

    public Student(String name, int age, String sex, String Patronymic)

    {

    super(name, age, sex); // вызываем конструктор родительского класса

    this.patronymic = patronymic;

    }
    public String toString()

    {

    return super.toString() + "\t" + patronymic; // вызываем реализацию родительского класса

    }

    }

    public class Example

    {

    public static void main(String[] args)

    {

    Student student = new Student("Andrey Klochkoff", 15, "male", "Teodorovich");

    System.out.println(student); // распечатаем на экране информацию о студенте

    }

    }
    Если метод объявлен с атрибутом final, это означает, что ни один расширенный класс не сможет переопределить данный метод с целью изменить его поведение. Другими словами, данная версия метода является окончательной.

    Подобным образом могут объявляться целые классы:

    final class NoExtending

    {

    // ...

    }

    Класс, помеченный с атрибутом final, не может являться родителем другого класса, а все его методы также неявно являются final.

    Интерфейс — не что иное, как именованное множество абстрактных полей и методов, которые лишь объявляются, но не определяются.

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

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

    В языке Java новый класс может расширять всего один суперкласс — такая модель носит название одиночного наследования. Расширение класса означает, что новый класс наследует от своего суперкласса не только контракт, но и реализацию. В некоторых объектно-ориентированных языках используется множественное наследование, при котором новый класс может иметь два и более суперклассов.

    Класс может реализовать произвольное количество интерфейсов. Интерфейсы, подобно классам, могут расширяться посредством ключевого слова extends. Интерфейс может расширить один или несколько других интерфейсов, добавить к ним новые константы и методы, которые должны быть реализованы в классе, реализующем расширенный интерфейс.

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

    Например:

    public interface IGod

    {

    void CreateHuman();

    void CreateHuman(String sex);

    void CreatePair(Human man);

    }

    Все классы, которые наследуют интерфейс IGod, обязаны реализовать все три метода, описанных в интерфейсе.

    Варианты заданий


    Вариант №1.

    1. Разработать иерархию людей, начиная от общего понятия «Человек» (абстрактный класс):

      1. Студент

      2. Родитель

      3. Ботаник

      4. Крутой родитель (выдает карманные деньги)

    2. Переопределить для каждого класса метод toString, возвращающий соответствующее описание человека (имя, возраст, пол, карманные деньги, среднюю оценку за сессию)

    3. Для каждого из классов сделать правильную реализацию метода CreatePair, создающего пару для данного человека по следующим правилам:

      1. По студенту — родителя (имя родителя берется из отчества студента)

      2. По родителю — студента (отчество студента берется из имени родителя)

      3. По ботанику — крутого родителя (количество карманных денег = 10 ^ (средняя оценка за сессию)

      4. По крутому родителю — ботаника (средняя оценка за сессию равна десятичному логарифму от карманных денег).

    4. Разработать программу для демонстрации классов.

    Вариант №2.

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

    2. Реализовать у животного метод поиска в лесу пропитания с последующим поеданием (уничтожением).

    3. Разработать программу для демонстрации классов.

    Необходимо учесть, что травоядные питаются только определенным видом растений, а хищники могут съесть только животных меньше себя.

    Вариант №3.

    1. Разработать иерархию из следующих классов, необходимых для реализации модели загрузки железнодорожного состава со склада:

      1. Классы вагонов 4-х разных типов: платформа для контейнеров, цистерны для жидкостей, для сыпучих грузов и автомобильная платформа;

      2. Классы продукции 4 разных типов: контейнеры, жидкости, сыпучие грузы и автомобили;

      3. Класс – железнодорожный состав, состоящий из вагонов разного типа;

      4. Класс – склад продукции

    2. Создать метод загрузки ж/д состава товарами со склада.

    3. Разработать программу, демонстрирующую описанную модель: загрузить со склада в состав следующую продукцию: нефть, дизельное топливо, мазут, зерно, уголь, песок, контейнеры и автомобили.

    Вариант 4.

    1. Разработать иерархию классов – коктейлей. Коктейль определяется ингредиентами, их количеством, а так же последовательностью действий по приготовлению (налить, размешать, взболтать, добавить). Действие необходимо реализовать в виде класса-перечисления.

    2. У класса коктейля предусмотреть метод, вычисляющий крепость коктейля.

    3. Разработать программу, демонстрирующую модель 1—2 коктейлей.

    Вариант 5.

    1. Разработать иерархию классов – транспортных средств: мотоцикл, автомобиль, грузовик, автобус, прицеп. У объектов – ТС имеются следующие свойства: марка, модель, максимальная допустимая масса перевозимого груза, кол-во пассажиров, максимальная скорость.

    2. Задать методы, с помощью которых можно получить значения свойств.

    3. Разработать программу для демонстрации классов.

    Необходимо учесть, что у ТС с прицепом общая допустимая снаряженная масса состоит из суммы значений буксирующего ТС и прицепа. Так же необходимо учесть, что по ПДД максимальная разрешенная скорость ТС с прицепом по на 20км/ч чем без него.

    Вариант 6.

    1. Разработать иерархию из следующих классов, необходимых для реализации модели зоопарка:

      1. Вольеры 4 типов: аквариумы, вольеры покрытые сеткой, открытые вольеры, вольеры с инфракрасным освещением.

      2. Животные 4 типов: водоплавающие, пернатые, копытные, хладнокровные.

    2. Для каждого из классов реализовать переопределенный метод Move() абстрактного класса «Животное» (обладающего общими параметрами: вес, возраст), который будет расселять животных в соответствующие вольеры.

    3. Разработать программу для демонстрации описанных классов.

    Вариант 7.

    1. Разработать модель пропускного автомобильного пункта из следующих классов:

      1. Машина: марка машины, максимальная скорость, радио, метод Move.

      2. Радио: текущая станция, статус работы (вкл/выкл), метод PlayTunes.

      3. Грузовик: вес, высота кузова.

      4. Пункт ДПС, пропускающий определенные машины.

    2. Определить метод Pass, при вызове которого будет производиться анализ, пройдет ли контроль данный класс машины (Пункт ДПС пропускает легковые и грузовые машины, едущие с определенным лимитом скорости; грузовые машины должны соответствовать максимальным параметрам веса и высоты кузова).

    3. Разработать программу для демонстрации описанных классов.

    Вариант 8.

    1. Разработать модель прачечной, состоящей из следующих классов:

      1. Белье: температура стирки, температура глажения.

      2. Цветное белье: цвет белья (перечисление: светлое, темное, цветное).

      3. Стиральная машина: тип порошка и кондиционера, цвет стираемого белья, температура стирки.

    2. Определить метод Load, который будет заполнять стиральную машину одним типом белья (с одинаковыми параметрами стирки).

    3. Переопределить метод toString у белья.

    4. Разработать программу для демонстрации описанных классов.


    Приложение. Методы класса Object. Сборщик мусора.


    Поскольку все классы являются явными или неявными расширениями класса Object, они наследуют его методы, которые делятся на 2 категории: общие служебные и методы, поддерживающие потоки. К категории служебных относятся следующие методы:

    public boolean equals(Object obj)
    Сравнивает объект-получатель с объектом, на который указывает ссылка obj; возвращает true, если объекты равны между собой, и false в противном случае. Если необходимо выяснить, указывают ли две ссылки на один и тот же объект, сравнивают их с помощью операторов == и !=. Метод equals же предназначен для сравнения значений.

    public int hashCode()
    Возвращает хеш-код для данного объекта. Каждому объекту может быть присвоен некоторый хеш-код, используемый при работе с хеш-таблицами. По умолчанию возвращается значение, которое является уникальным для каждого объекта. Оно используется при сохранении объектов в таблицах Hashtable.

    protected Object clone() throws CloneNotSupportedException
    Возвращает дубликат объекта. Дубликатом называется новый объект, являющийся копией объекта, для которого вызывался метод clone.

    public final Class getClass()
    Возвращает объект типа Class, который соответствует классу данного объекта. Во время выполнения программы на Java можно получить информацию о классе в виде объекта Class, возвращаемого методом getClass.

    protected void finalize() throws Throwable
    Завершающие операции с объектом, осуществляемые во время сборки мусора. Неиспользуемые объекты Java автоматически уничтожаются сборщиком мусора, который работает в фоновом режиме и следит за ссылками на объекты. Когда ссылок на объект больше не остается, появляется возможность убрать его из кучи, где он временно хранился, хотя само удаление может быть отложено до более подходящего момента.

    Методы hashCode и equals должны переопределяться, если нужно ввести новую концепцию равенства объектов, отличающуюся от принятой в классе Object. По умолчанию считается, что два различных объекта не равны между собой, а их хеш-коды не должны совпадать. Если ваш класс вводит концепцию равенства, при которой два различных объекта могут считаться равными, метод hashCode должен возвращать для них одинаковые значения хеш-кода.


    написать администратору сайта