arrow-left

All pages
gitbookPowered by GitBook
1 of 21

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

2. Основы синатксиса Java

При разработке языка Java был взят за основу синтаксис языков С и C++, поэтому, многие аспекты синтаксиса языка покажутся вам знакомыми.

hashtag
Комментарии

В Java, как и в C, существуют однострочные и блоковые комментарии. Однако, кроме этого, согласно конвенции Oracle, существуют другие виды комментариев: copyright-блок вверху, doc-комментарии, TODO-комментарии, комментарии после statement`ов, комментарии для комментирования кода. Ознакомьтесь с принятыми правилами использования комментариев, на защите лабораторных они будут требоваться в обязательном порядке, согласно принятым конвенциям.

hashtag
Переменные

Переменные чувствительны к регистру (variable и Variable – две разные переменные), могут быть бесконечной длины, состоять из букв юникода и цифр юникода, символов _ и $.

Первый символ переменной может быть буквой, символом _ или $ (использовать _ или $ первым символом КРАЙНЕ НЕ РЕКОМЕНДУЕТСЯ, они существуют для специальных ситуаций, которые нас сейчас не интересуют, так что считайте, что начинаться переменная может только с буквы юникода). Крайне не рекомендуется использовать буквы национальных алфавитов, кириллицу, транслит. Только латинские буквы, названия на английском. Также, названия переменных не должны совпадать со списком зарезервированных слов, который представлен ниже.

Кроме ключевых слов в Java существуют три литерала: null, true, false, не относящиеся к ключевым и зарезервированным словам, а также зарезервированное слово var, значение которого зависит от его позиции в коде.

Приведенный ниже блок кода даст вам общее представление о том, как надо называть переменные

hashtag
Типы данных

В языке Java существуют примитивные типы (аналогичны типам данных в C) и ссылочные (или объектные) типы данных. На данный момент нас интересуют только примитивные типы данных.

Java – строго типизированный язык программирования. Это значит, что переменная, перед использованием, должна быть объявлена и ей должен быть присвоен тип, который нельзя поменять. Также, при выполнении операций присваивания, компилятор проверяет соответствие типов (какого-то механизма автоматического приведения типов у Java нет).

Всего существуют восемь примитивных типов данных: int, long, short, byte, double, float, char, boolean. Их очень легко запомнить:

  • 4 типа для целых чисел («короткое short», «среднее int», «длинное long» и байт);

  • 2 типа для чисел с плавающей запятой (старая парочка double и float);

  • 2 «специальных» типа – символ и булевый тип.

hashtag
Операторы ветвления

Операторы ветвления в C и Java практически идентичны

hashtag
Циклы

Работа с циклами в Java мало чем отличается от языка C

hashtag
Массивы

Работа с массивами в Java несколько отличается от работы с массивами в C, в основном, из-за механизма выделения памяти под массивы.

hashtag
Методы

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

1. Установка программного обеспечения, создание и запуск проекта

Для работы с Java, вам нужно установить JDK и среду разработки.

hashtag
Установка JDK

Java Development Kit (сокращенно JDK) — бесплатно распространяемый компанией Oracle Corporation (ранее Sun Microsystems) комплект разработчика приложений на языке Java, включающий в себя компилятор Java (javac), стандартные библиотеки классов Java, примеры, документацию, различные утилиты и исполнительную систему Java (JRE).

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

hashtag
Тема: Создание классов и объектов

hashtag
Цель лабораторной работы:

Задание на лабораторную работу

hashtag
Как выполнять лабораторную работу

Скачайте архив с заданием на первую лабораторную работу.

Откройте IntelliJ IDEA (предварительно установите плагин EduTools), выберите пункт Learn and Teach -> Import Local Course

package ai201.ivanov;

/**
 * Это специальный комментарий для документирования (Doc comment).
 * С помощью специальной утилиты (javadoc), такие комментарии можно превратить в HTML-странички,
 * которые вместе создают документацию к вашей программе.
 * Для удобства, здесь можно использовать специальные ссылки {@link Main} и <i>HTML-тэги</i>
 */
public class Main {

    /**
     * Этот метод является "точкой входа" приложения. В проекте может быть только один метод с такой сигнатурой
     * @param args аргументы при запуске приложения
     */
    public static void main(String[] args) {

        // Это однострочный комментарий (single line comment)

        /*
         * Это комментарий в виде блока (block comment)
         */

        // TODO: это специальный TODO-комментарий. Тут можно описать, что нужно доделать на каком-то участке кода
        // FIXME: это тоже TODO-комментарий, обычно тут мелкие баги и что нужно исправить на данном участке кода

        // TODO:2019-09-03:NickGodov: дописать вывод данных в файл

        int my_variable = 5;            /* Комментарии после statement должны быть выровнены слева */
        int my_other_variable = 999;    /* с помощью табуляции */

        // Если нужно закомментировать код, то каждая строчка комментируется однострочным комментарием
//        int my_old_variable = 100;
//        int my_other_old_variable = 200;

        // Перед комментарием принято оставлять пустую строку
        int number = 924;
    }
}

изучить синтаксис и процесс создания классов;

  • научиться создавать объекты созданных классов.

  • boolean

    do

    if

    private

    this

    break

    double

    implements

    protected

    throw

    byte

    else

    import

    public

    throws

    case

    enum

    instanceof

    return

    transient

    catch

    extends

    int

    short

    try

    char

    final

    interface

    static

    void

    class

    finally

    long

    strictfp

    volatile

    const

    float

    native

    super

    while

    record

    var

    yield

    16 bit

    0

    short b = 10000;

    int

    -2^31

    -2^31-1

    32 bit

    0

    int a = 15;

    int aHex = 0xaa;

    int aBin = 0b0001111;

    (это же справедливо и для byte,short,long, если соблюдать диапазоны)

    long

    -2^63

    -2^63-1

    64 bit

    0L

    long number = 10000L;

    double

    4.9^-324

    ~1.8^308

    64 bit

    0.0d

    double d = 6.6;

    float

    ~1.4^-45

    ~3.4^38

    32 bit

    0.0f

    float f = 5.5f;

    char

    0

    65535

    16 bit

    '\u0000'

    char c = ‘f’;

    char c = 63;

    char c = '\u2422';

    boolean

    false

    true

    1 bit

    false

    boolean b = true;

    abstract

    continue

    for

    new

    switch

    assert

    default

    goto

    package

    Type

    Min

    Max

    RAM

    Default

    Объявления и литералы

    byte

    -128

    127

    8 bit

    0

    byte b = 100;

    short

    -32,768

    synchronized

    32,767

    int spd         = 25;   // ПЛОХО: Можно, но не рекомендуется, т.к. название не информативно
    int carminspd   = 25;   // ПЛОХО: Не экономьте на названиях переменных!
    int carMinSpeed = 25;   // ХОРОШО: Название переменной говорит само за себя
    
    int s = 0; // МОЖНО: однобуквенные допускаются, только если это какие-то короткоживущие бросовые переменные
    
    int speed = 150;    // ХОРОШО: Нормально, понятно, что переменная отвечает за скорость
    int Speed = 150;    // ПЛОХО: Крайне не рекомендуется, переменные не должны начинаться с капса
    int SPEED = 150;    // ПЛОХО: Крайне не рекомендуется, полностью капсом пишутся константы
    
    const int MIN_SPEED = 25; // НЕЛЬЗЯ: в Java const не используется (хотя это зарезервированное слово)
    final int MIN_SPEED = 25; // ХОРОШО: в Java для констант используется final
    
    int моя_переменная   = 356;   // ПЛОХО: Только латиница
    int DŽDžDzȯȺώϷЂ        = 145;   // ПЛОХО: Только латиница
    int moya_peremennaya  = 29;    // ПЛОХО: Транслит - это полный моветон, только английский!
    
    int $myvar = 100;   // ПЛОХО:  Теоретически можно, но НЕ РЕКОМЕНДУЕТСЯ
    int _myvar = 100;   // ПЛОХО:  Теоретически можно, но НЕ РЕКОМЕНДУЕТСЯ
    int 2pac   = 0;     // НЕЛЬЗЯ: с цифры начинать нельзя
    int %d     = 5;     // НЕЛЬЗЯ: с других знаков начинать нельзя
    int 'f'    = 5;     // НЕЛЬЗЯ: с кавычек начинать нельзя
    
    // Если название переменной состоит из двух слов
    int max_speed       = 150;  // ПЛОХО: Использовать _ для отделения слов не в константах не рекомендуется
    int MaxSpeed        = 150;  // ПЛОХО: Крайне не рекомендуется, переменные не должны начинаться с заглавной буквы
    int maxSpeed        = 150;  // ХОРОШО: Вот так нормально, используется lowerCamelCase
    final int MAX_SPEED = 150;  // ХОРОШО: Константы пишутся капсом, каждое слово отделяется _
    int a = 5;
    int b = 4;
    int min;
    
    // Так нужно оформлять обычный if
    if (a >= b) {
        min = b;
    }
    
    // Так нужно оформлять if-else
    if (a >= b) {
        min = b;
    } else {
        min = a;
    }
    
    // Так нужно оформлять if-else if-else
    if (a > b) {
        min = b;
    } else if (a < b) {
        min = a;
    } else {
        min = a;
    }
    
    // В Java используется тернарный оператор
    min = (a >= b) ? b : a;
    
    // Это равнозначно следующему выражению
    if (a >=b) {
        min = b;
    } else {
        min = a;
    }
    
    // Так оформляется switch
    switch (a) {
        case 1:
            // что-то делаем
            break;
        case 2:
            // делаем что-то другое
            break;
        default:
            // это выполняется в том случае, если ни одно из кейсов не выполнился
    }
    int progression = 0;
    
    // Так оформляется for
    for (int i=0; i < 5; i++) {
        progression +=i;
    }
    
    // ПЛОХО: так оформлять циклы не рекомендуется
    for (int i=0; i < 5; i++) progression +=i;
    
    // МОЖНО: если тело цикла состоит из одного statement'а, то можно опустить
    // фигурные скобки
    for (int i=0; i < 5; i++)
        progression +=i;
    
    // Пустой for
    for (int j=0; j < 10; j++);
    
    // Так оформляется while
    int iterator = 0;
    while (iterator < 10) {
        // делаем что-то в цикле
        iterator++;
    }
    
    // Так оформляется do-while
    int loops = 10;
    do {
        // что-то делаем
        loops--;
    } while (loops > 0);
    
    // Также, в Java есть аналог foreach
    int[] array = { 1, 2, 3, 4, 5 };
    int sum = 0;
    for(int i : array) {
        sum += i;
    }
    
    // Этот же цикл можно представить обычным for`ом
    for(int i = 0; i < 5; i++) {
        sum += array[i];
    }
    // Объявление массивов
    
    /*
     * ХОРОШО: согласно всем соглашениям по коду и различным рекомендациям, квадратные скобки
     * ставят ПОСЛЕ ТИПА ДАННЫХ
     */
    int[] goodArray;
    
    /*
     * ПЛОХО: компилятор не выдаст ошибку, но такой синтаксис делает код менее читабельным
     */
    int badArray[];
    
    /*
     * НЕЛЬЗЯ: при объявлении массива нельзя указать его размерность.
     * Java не выделит память, пока массив не будет инициализирован
     */
    int[5] anotherBadArray;
    
    // Объявления многомерных массивов
    
    int [][] twoDimensionalArray;
    int [][][] threeDimensionalArray;
    
    // Инициализация массивов
    
    goodArray = new int[10];    // Инициализируем массив с 10 элементами
    goodArray[0] = 15;          // Присваиваем значение первому элементу массива
    goodArray[1] = 25;          // Присваиваем значение второму элементу массива
    
    twoDimensionalArray = new int [5][4];   // Двумерный массив 5х
    twoDimensionalArray[0] = new int[4];
    twoDimensionalArray[1] = new int[8];    // ПЛОХО: Компилятор проглотит, но по факту выделится место всего под 4 инта
    twoDimensionalArray[0][0] = 1; // Присваиваем значение
    twoDimensionalArray[1][5] = 5; // НЕЛЬЗЯ: Компилятор выдаст ошибку
    
    System.out.print(twoDimensionalArray[1][6]); // НЕЛЬЗЯ: Компилятор выдаст ошибку
    
    // Объявление с инициализацией
    
    int[] quickArray = {1, 2, 3 ,4};    // Объявляем и сразу заполняем данные. Компилятор выделит место под 4 инта
    quickArray[5] = 6;                  // НЕЛЬЗЯ: Компилятор выдаст ошибку, т.к. индекс выходит за пределы массива
    
    int[][] quick2DArray = {
            {1 ,2 ,3},
            {1, 3, 4}
    };
    /*
     * Синтаксис функции:
     * [1-модификаторы доступа] [2-тип возвращаемого значения] [3-имя]([4-аргументы]) [5-список исключений] {
     *      6- тело функции
     * }
     *
     * 1 - модификаторы доступа: на данный момент они нас не интересует, можно ничего не писать или писать private
     * 2 - тип возвращаемого значения - тип данных либо void, если функция ничего не возвращает
     * 3 - ограничения как и на имена переменных, но есть дополнительные правила наименования, о них ниже
     * 4 - список аргументов через запятую. Например (int a, double b). Если нет аргументов - пустые скобки
     * 5 - исключение пока не рассматриваем, если их нет, то просто ничего не пишут
     * 6 - тело функции, в нем происходит выполнение функции. Если есть возвращаемый тип данных - должен быть return
     */
    private int findMinimum(int a, int b) {
            int min;
    
            min = (a < b) ? a : b;
            return min;
            }
    
    /*
     * Название метода начинается с маленькой буквы, если несколько слов - используется lowerCamelCase.
     * Первое слово должно быть глаголом (т.к. метод, как правило, "что-то делает"), остальные слова могут быть
     * прилагательными, существительными и тд. Символ _ крайне желательно не использовать (кроме юнит-тестов)
     */
    
    // ХОРОШО: с маленькой буквы, первое слово - глагол
    private void drawCircle() {}
    
    // ПЛОХО: Символы $ и _ не используем
    private void $er () {}
    
    // ПЛОХО: используйте camelCase для названия методов
    private void draw_circle () {}
    
    // ПЛОХО: название метода должно начинаться с маленькой буквы
    private void Draw() {}
    
    // ПЛОХО: первое слово должно быть глаголом
    private void circle() {}
    Для установки JDK необходимо скачать установщик по следующей ссылкеarrow-up-right (также вы можете загуглить "download jdk" и перейти по первой ссылке).

    Установка JDK, как правило, проходит без проблем, настраивать параметры установки не требуется.

    hashtag
    Установка среды разработки

    Интегрированная среда разработки (Integrated development environment — IDE) — комплекс программных средств, используемый программистами для разработки программного обеспечения.

    Для Java существует "большая тройка" сред разработки:

    • ​IntelliJ IDEAarrow-up-right​

    • ​NetBeansarrow-up-right​

    • ​Eclipsearrow-up-right​

    Все остальные среды являются более простыми и менее функциональными.

    В рамках данного курса будет использоваться среда IntelliJ IDEA, которая считается самой популярной IDE для Java. Вы можете выполнять лабораторные работы любым удобным вам способом, в любой удобной вам среде разработки.

    В рамках данного курса будет использована Community версия среды IntelliJ IDEA. Скачиваем и запускаем с сайта разработчика установщик.

    В процессе установки желательно выбрать следующие параметры установки

    hashtag
    Добавление плагинов для выполнения лабораторной работы

    Кроме установки среды разработки, нам необходимо добавить дополнительные плагины - EduTools и CheckStyle.

    Запустим IntelliJ IDEA, после чего выберем пункт Configure -> Plugins.

    Найдите в поисковой строке EduTools и установим его.

    После этого введем в поисковой строке CheckStyle и также установим его. После этого необходимо перезапустить среду разработки.

    hashtag
    Создание проекта и запуск приложения

    При первом запуске вас попросят выбрать цветовую схему среды разработки. Остальные параметры можете опустить. В стартовом окне выбираем пункт "Create New Project".

    В следующем окне обратите внимание, что вас просят выбрать JDK, который будет использован в вашем проекте.

    Нажмите кнопку "New..." и укажите путь к установленному ранее JDK (как правило, это C:\Program Files\Java\xxxx)

    В следующем окне выбираем пункт "Create project from template" и выбираем "Command Line App"

    Далее нам необходимо указать название проекта, а также Base Package проекта. Далее мы разберемся, что означает термин package, а пока можете указать его в формате [группа].[фамилия]

    После завершения мастера нового проекта, IDEA сгенерирует новый пустой проект. Сразу запустим проект. Для этого можно выбрать пункт Run -> Run "Main", нажать комбинацию клавиш Shift+F10 либо нажать на зеленый треугольник справа в верхней части редактора.

    Выберите архив с заданием

    Откройте задание с помощью кнопки Open

    Задание на лабораторную работу состоит из 7 задач, которые необходимо решить

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

    hashtag
    Пример выполнения лабораторной работы

    Давайте рассмотрим выполнение лабораторной работы на примере первой задачи.

    Нам необходимо реализовать метод sleepIn(). Согласно условию задачи, метод принимает на вход булеву переменную weekday (будний ли день) и переменную vacation (отпуск или нет).

    Давайте нажмем кнопку Check и посмотрим, что произойдет.

    Как мы видим, изначально задача не решена, нам необходимо корректно реализовать метод.

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

    Проверим правильность написания кода.

    Ура, задача решена. Для успешной сдачи лабораторной работы необходимо выполнить все 7 задач.

    hashtag
    Условия задач

    На всякий случай, продублируем условия задач на первую лабораторную работу

    hashtag
    1. Метод sleepIn()

    Метод sleepIn() принимает решение о том, необходимо ли нам проснуться или можно еще поспать. Нужно проснуться, если будний день и нет отпуска

    • Параметр weekday определяет, будний ли день

    • Параметр vacation определяет, отпуск или нет

    Метод возвращает true, если можно еще поспать и false в обратном случае

    hashtag
    2. Метод closeToTen()

    Дано два числа, метод closeToTen() возвращает число, которое наиболее близко к 10. Если оба числа равноблизки к 10, то метод возвращает 0.

    Подсказка: используйте метод Math.abs() для получения абсолютного значения числа. Пример:

    • closeToTen(8, 13) -> 8 (число 8 ближе к 10)

    • closeToTen(8, 11) -> 11 (число 11 ближе к 10)

    • closeToTen(13, 7) -> 0 (оба числа равноблизки к 10)

    hashtag
    3. Метод reverseArray()

    Метод принимает на вход массив и возвращает массив в обратном порядке

    Пример:

    • reverseArray([1,2,3]) -> [3,2,1]

    • reverseArray([7,0,0]) -> [0,0,7]

    hashtag
    4. Метод biggestArray()

    Метод принимает на вход два массива и возвращает массив, сумма элементов которого больше. Если суммы элементов массивов равны, метод возвращает первый массив.

    Пример:

    • biggestArray([1,2,3], [1,2,4]) -> [1,2,4]

    • biggestArray([1,5,10], [15]) -> [1,5,10]

    • biggestArray([2,4,6], [10,2]) -> [2,4,6]

    hashtag
    5. Метод seriesUp()

    Метод принимает на вход число n >=0. Метод возвращает массив по шаблону {1, 1,2, 1,2,3, ...1,2,3..n}. Длина итогового массива = n*(n+1)/2

    Пример:

    • seriesUp(3) -> [1, 1, 2, 1, 2, 3]

    • seriesUp(4) -> [1, 1, 2, 1, 2, 3, 1, 2, 3, 4]

    • seriesUp(2) -> [1, 1, 2]

    hashtag
    6. Метод canSplit()

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

    Пример:

    • canSplit([1, 1, 1, 2, 1]) -> true ([1,1,1] [2,1])

    • canSplit([2, 1, 1, 2, 1]) -> false

    • canSplit([10, 1, 2, 3, 4]) -> true ([10] [1,2,3,4])

    hashtag
    7. Метод scoresIncreasing()

    Дан массив, метод возвращает true если каждый элемент массива равен или больше предыдущему

    Пример:

    • scoresIncreasing([1, 3, 4]) -> true

    • scoresIncreasing([1, 3, 2]) -> false

    • scoresIncreasing([1, 1, 4]) -> true

    hashtag
    Проект - игра "Крестики-нолики"

    Реализуйте игру "Крестики-нолики". В игру играют два человека, поочередно вводя координаты поля, куда необходимо поставить крестик или нолик. Ввод происходит в формате [строка] [столбец]. Например: 0 0 или 1 2.

    Сценарий игры будет примерно следующим

    Ниже представлен возможный вариант метода для отрисовки поля. В данном случае подразумевается, что в двумерном массиве число 0 соответствует пустому полю, число 1 соответствует X, а число - 1 соответствует 0.

    Блок-схема алгоритма программы может быть примерно следующей.

    circle-exclamation

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

    Если пользовательский ввод оказался некорректным, то программа просит игрока повторить ход.

    file-archive
    4KB
    javalab1.zip
    archive
    arrow-up-right-from-squareOpen

    Синтаксис создания классов

    hashtag
    Базовые сведения об объектах и классах

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

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

    circle-info

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

    Класс – шаблон для объектов. Каждый объект является экземпляром (instance) какого-либо класса («безклассовых» объектов не существует). В рамках класса задается общий шаблон, структура, на основании которой создаются объекты. Данные, относящиеся к классу, называются полями класса, а программный код для их обработки называется методами класса. Поля и методы называют общим термином – члены класса.

    Разница между классом и объектом такая же, как между абстрактным понятием и реальным объектом.

    Объект состоит из следующих частей:

    • имя объекта;

    • состояние (переменные состояния). Данные, содержащиеся в объекте, представляют его состояние. В терминологии ООП эти данные называются атрибутами. Например, атрибутами работника могут быть: имя, фамилия, пол, дата рождения, номер телефона. В разных объектах атрибуты имеют разное значение. Фактически, в объектах определяются конкретные значения тех переменных (полей класса), которые были заявлены при описании класса;

    • методы (операции) – применяются для выполнения операций с данными, а также для совершения других действий. Методы определяют, как объект взаимодействует с окружающим миром.

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

    В объектно-ориентированной программе весь код должен находиться внутри классов!

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

    hashtag
    Классы как абстрактные типы данных

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

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

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

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

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

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

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

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

    Класс является типом данных, определяемым пользователем. В классе задаются свойства и поведение какого-либо предмета или процесса в виде полей данных (аналогично структуре) и функций для работы с ними. Создаваемый тип данных обладает практически теми же свойствами, что и стандартные (примитивные) типы (тип задает внутреннее представление данных в памяти компьютера, множество значений, которое могут принимать величины этого типа, а также операции и функции, применяемые к этим величинам).

    Пользовательские типы данных принято называть абстрактными типами данных

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

    Конкретные переменные типа данных "класс" называются экземплярами класса, или объектами. Объекты взаимодействуют между собой, посылая и получая сообщения. Сообщение - это запрос на выполнение действия, содержащий набор необходимых параметров. Механизм сообщений реализуется с помощью вызова соответствующих функций.

    hashtag
    Пример использования объектного подхода

    Создадим класс Box, который описывает контейнер, допустим, на каком-то складе.

    Класс Box определяет три переменные экземпляра: width (ширина), height (высота) и depth (глубина). В настоящий момент класс Box не содержит никаких методов.

    Как мы уже говорили, класс определяет новый тип данных. В данном случае новый тип данных называется Box. Это имя будет использоваться для объявления объектов типа Box. Не следует забывать, что объявление class создает только шаблон, но не конкретный объект. Таким образом, приведенный выше код не приводит к появлению каких-нибудь объектов типа Box.

    Чтобы действительно создать объект класса Box, нужно воспользоваться оператором new

    После выполнения этого оператора объект myBox станет экземпляром класса Box. Таким образом, он обретет "физическое" существование.

    Также следует напомнить, что каждый объект содержит собственную копию переменной экземпляра, которая определена в классе. Каждый объект типа Boxбудет содержать собственные копии переменных width, height и depth(рис. 4.2).

    Изменения в переменных экземпляра одного объекта не влияют на переменные экземпляра другого объекта. Таким образом, каждый объект класса Box будет содержать собственные копии переменных width, height и depth. Для доступа к этим переменным служит оператор-точка (.). Эта операция связывает имя объекта с именем переменной экземпляра. Например, чтобы присвоить переменной width экземпляра myBox значение 100, нужно выполнить следующий оператор:

    Этот оператор предписывает компилятору, что копии переменной width, хранящейся в объектe myBox, требуется присвоить значение 100. В общем, операция-точка служит для доступа как к переменным экземпляра, так и к методам в пределах объекта.

    Ниже приведет пример программы, в которой используется класс Box

    Как пояснялось ранее, каждый объект содержит собственные копии переменных экземпляра. Это означает, что при наличии двух объектов класса Box каждый из них будет содержать собственные копии переменных width, height и depth. Следует, однако, иметь ввиду, что изменения в переменных экземпляра одного объекта не влияют на переменные экземпляра другого. Например, в следующей программе объявлены два объекта класса Box:

    Программа выводит следующий результат:

    Как видите, данные из объекта myBox1 полностью изолированы от данных, содержащихся в объекте myBox2.

    hashtag
    Методы класса

    Как упоминалось ранее, классы состоят из двух компонентов: переменных экземпляра и методов. Общая форма метода выглядит следующим образом:

    где возвращаемый тип означает конкретный тип данных, возвращаемый методом. Он может быть любым допустимым типом данных, в том числе и типом созданного класса. Если метод не возвращает значение, то его возвращаемым типом должен быть void. В качестве имени методов может быть любой допустимый идентификатор, кроме тех, которые уже используются другими элементами кода в текущей области действия. А список параметров обозначает последовательность пар "тип-идентификатор", разделенных запятыми. По существу, параметры - это переменные, которые принимают значения аргументов, передаваемых методу во время его вызова. Если у метода отсутствуют параметры, то список параметров оказывается пустым. Методы, возвращаемый тип которых отличается от void, возвращают значение вызывающей части программы с помощью оператора return.

    Вернемся к нашему примеру с классом Box. Было бы логично, если бы расчет объема коробки выполнялся в классе Box, поскольку объем коробки зависит от ее размеров. Для этого добавим в класс Box метод getVolume()

    Внимательно рассмотрим две следующие строки кода

    В первой строке вызывается метод volume() для объекта myBox1. Следовательно, метод volume() вызывается по отношению к объекту myBox1, для чего было указано имя объекта, а вслед за ним - операция-точка. Таким образом, в результате вызова метода myBox1.volume() выводится объем коробки, определяемого объектом myBox1, а в результате вызова метода myBox2.volume() - объем коробки, определяемого объектом myBox2.

    При вызове метода myBox1.volume() исполняющая система Jаvа передает управление коду, определенному в теле метода volume(). По окончании выполнения всех операторов в теле метода управление возвращается вызывающей части программы и далее ее выполнение продолжается со строки кода, следующей за вызовом метода. В самом общем смысле метод - это способ реализации подпрограмм в Java.

    В методе volume() следует обратить внимание на еще одну очень важную особенность: ссылка на переменные экземпляра width, height и depth делается непосредственно без указания перед ними имени объекта или операции-точки. Когда в методе используется переменная экземпляра, определенная в его же классе, это делается непосредственно, без указания явной ссылки на объект и применения операции-точки . Это становится понятным, если немного подумать. Метод всегда вызывается по отношению к какому-то объекту его класса. Как только этот вызов сделан, объект известен. Таким образом, в теле метода вторичное указание объекта совершенно излишне. Это означает, что переменные экземпляра width, height и depth неявно ссылаются на копии этих переменных, хранящиеся в объекте, который вызывает метод volume().

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

    Следует обратить внимание, что метод getVolume() возвращает значение 3000, и это значение рассчитанного объема сохраняется в переменной vol. При обращении с возвращаемыми значениями следует принимать во внимание два важных обстоятельства:

    • тип данных, возвращаемых методом, должен быть совместим с возвращаемым типом, указанным в методе. Так, если какой-нибудь метод должен возвращать логический тип boolean, то возвратить из него целочисленное значение нельзя;

    • переменная, принимающая возвращаемое методом значение (например, vol), также должна быть совместима с возвращаемым типом, указанным для метода.

    public boolean sleepIn(boolean weekday, boolean vacation) {
        return !weekday || vacation;
    }
    ---------
    |       |
    |       |
    |       |
    ---------
    First player: 0 0
    ---------
    | X     |
    |       |
    |       |
    ---------
    Second player: 1 1
    ---------
    | X     |
    |   0   |
    |       |
    ---------
    First player: 0 1
    ---------
    | X X   |
    |   0   |
    |       |
    ---------
    Second player: 2 2
    ---------
    | X X   |
    |   0   |
    |     0 |
    ---------
    First player: 0 2
    First player won!
    
    Process finished with exit code 0
    Main.java
        public static void drawField(int[][] array) {
            System.out.println("---------");
            for (int i = 0; i < 3; i++) {
                System.out.print("| ");
                for (int j = 0; j < 3; j++) {
                    String symbol = " ";
                    if (array[i][j] != 0)
                        symbol = array[i][j] == 1 ? "X" : "0";
                    System.out.print(symbol + " ");
                }
                System.out.println("|");
            }
            System.out.println("---------");
        }
    Представление объектов в памяти
    Box.java
    public class Box {
        
        double width;
        double height;
        double depth;
        
    }
    Main.java
    public class Main {
        public static void main(String[] args) {
    
            Box myBox = new Box();
        }
    }
    myBox.width = 100;
    public class Main {
        public static void main(String[] args) {
    
            // Создаем объект типа Box
            Box myBox = new Box();
    
            // Присваиваем значения переменным экземпляра myBox
            myBox.width = 10;
            myBox.height = 20;
            myBox.depth = 15;
    
            // Рассчитываем объем коробки
            double volume = myBox.width * myBox.height * myBox.depth;
    
            System.out.println("Объем равен: " + volume);
        }
    }
    public class Box {
    
        double width;
        double height;
        double depth;
    
    }
    public class Main {
        public static void main(String[] args) {
    
            Box myBox1 = new Box();
            Box myBox2 = new Box();
    
            // Присваиваем значения для mybox1
            myBox1.width = 10;
            myBox1.height = 20;
            myBox1.depth = 15;
    
            // Присваиваем значения для mybox2
            myBox2.width = 3;
            myBox2.height = 6;
            myBox2.depth = 9;
    
            double volume;
    
            // объем первой коробки
            volume = myBox1.width * myBox1.height * myBox1.depth;
            // будет выведено 3000
            System.out.println("Объем равен: " + volume);
    
            // объем второй коробки
            volume = myBox2.width * myBox2.height * myBox2.depth;
            // будет выведено 162
            System.out.println("Объем равен: " + volume);
        }
    }
    Объем равен: 3000.0
    Объем равен: 162.0
    [возвращаемый тип] имя ([список параметров]) {
        [тело метода]
    }
    Box.java
    class Box {
    
        double width;
        double height;
        double depth;
        
        void getVolume() {
            System.out.print("Объем коробки равен ");
            System.out.println(width * height * depth);
        }
    }
    mybox1.volume();
    mybox2.volume();

    Инкапсуляция

    Инкапсуляция в Java реализована с помощью использования модификаторов доступа.

    Язык Java предоставляет несколько уровней защиты, которые позволяет настраивать область видимости данных и методов. В Java имеется четыре категории видимости элементов класса:

    • private– члены класса доступны только членам данного класса. Всё что объявлено private, доступно только конструкторам и методам внутри класса и нигде больше. Они выполняют служебную или вспомогательную роль в пределах класса и их функциональность не предназначена для внешнего пользования. Закрытие (private) полей обеспечивает инкапсуляцию;

    • по умолчанию (package-private) – члены класса доступны классам, которые находятся в этом же пакете;

    • protected– члены класса доступны классам, находящимся в том же пакете, и подклассам – в других пакетах;

    • public– члены класса доступны для всех классов в этом и других пакетах.

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

    Когда член класса обозначается модификатором доступа public, он становится доступным для любого другого кода в программе, включая и методы, определенные в других классах.

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

    При отсутствии модификатора доступа, члены класса доступны другим членам класса, который находится в этом же пакете.

    Модификатор доступа protected связан с использованием механизма наследования и будет рассмотрен позже.

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

    Член класса (переменная, конструктор, методы), объявленный public, доступен из любого метода вне класса.

    Всё что объявлено private, доступно только конструкторам и методам внутри класса и нигде больше. Они выполняют служебную или вспомогательную роль в пределах класса и их функциональность не предназначена для внешнего пользования. Закрытие (private) полей обеспечивает инкапсуляцию.

    hashtag
    Сокрытие полей класса

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

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

    Геттер возвращает значение приватного поля, тогда как сеттер меняет значение приватного поля (новое значение передается в качестве аргумента метода).

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

    Геттер должен иметь префикс get, после которого идет название поля с большой буквы. Геттер, как правило, не имеет входных аргументов.

    Сеттер должен иметь префикс set, после которого идет название поля с большой буквы. Сеттер принимает на вход новое значение поля. Возвращаемый тип, как правило, void.

    circle-info

    Большинство IDE для Java имеют механизм для генерации геттеров и сеттеров. В IntelliJ IDEA нажмите комбинацию Alt+Insert находясь в окне редактирования java-файла. Откроется контекстное меню Generate, где вы можете выбрать генерацию геттера и сеттера, после чего указать поля, для которых необходимо сгенерировать методы.

    hashtag
    5. Пример использования инкапсуляции

    Представим, что нам необходимо создать класс «Корзина» (Cart), который хранит в себе набор объектов класса «Товар» (Item).

    Какие методы «Корзина» должна предоставлять для внешнего использования? Это могут быть, например, методы «Добавить товар», «Убрать последний добавленный товар», «Подсчет суммы цен товаров в корзине», «Повышение цен в корзине на N процентов» и «Снижение цен в корзине на N процентов».

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

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

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

    То – как будет реализовано хранение товаров в корзине – это внутренняя логика класса и она не должна быть доступна внешнему миру, она должна быть скрыта от внешнего вмешательства. Другие классы, которые будут использовать объекты класса Cartне должны знать и не должны иметь доступ к тому – как там «внутри» реализовано хранение товаров, подсчет цен и изменение цены на определенный процент и так далее, они могут только лишь использовать предоставленные им публичные методы. Давайте реализуем «Корзину» с помощью структуры «стек», которая, в свою очередь, реализована обычным массивом.

    Как мы видим, массив с товарами, указать на вершину стек объявлены как privateчлены класса. Это значит, что мы не можем получить к ним доступ извне – они доступны только внутри данного класса.

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

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

    В дальнейшем мы можем переделать класс Cart и поменять внутреннюю реализацию. Мы можем использовать структуру "очередь", мы можем использовать коллекции, мы можем иначе реализовать операции добавления и удаления элемента в стеке, но если мы сохраним интерфейс класса неизменным, то для внешнего мира эти изменения внутренней логики не будут важны и если мы поменяем внутреннюю логику одного небольшого участка программы, то вся остальная программа будет работать так же.

    Снизить цены товаров в корзине на определенный процент (значение процента передается как аргумент метода).

    Название метода

    Описание

    public Cart(int capacity)

    Конструктор с 1 параметром – максимальным количеством товаров в корзине.

    public boolean addItem(Item item)

    Добавление товара в корзину. Возвращает успешность операции.

    public Item deleteLastAddedItem()

    Удаление последнего добавленного товара в корзину. Возвращает удаленный товар.

    public double calculateItemPrices()

    Подсчет суммы цен всех товаров в корзине.

    public void raiseItemPrices(double percent)

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

    public void cutItemPrices(double percent)

    public String errMessage;
    private AccountBalance balance;
    
    private boolean isError(byte status) {}
    public class Account {}
    Account.java
    public class Account {
    
        private double balance;
    
        public double getBalance() {
            return balance;
        }
    
        public void setBalance(double balance) {
            this.balance = balance;
        }
    }
    Cart cart = new Cart();
    cart.addItem(new Item("Клавиатура", 2000));
    Cart.java
    public class Cart {
    
        private Item[] stack; // массив для реализации стека
        private int topIndex; // указатель на вершину стека
    
        // При создании корзины мы должны
        // указать максимальное количество элементов
        // в корзине
        public Cart(int capacity) {
            stack = new Item[capacity];
            topIndex = -1;
        }
    
        // Добавление нового товара в корзину
        public boolean addItem(Item item) {
            return push(item);
        }
    
        // Приватный метод, который реализует добавление в стек
        private boolean push (Item item) {
            // Добавляем товар в стек
            return true; // или false если не стек переполнен
        }
        
        // Удаление последнего добавленного товара в корзину
        public Item deleteLastAddedItem() {
            return pop();
        }
    
        // Приватный метод, который реализует извлечение из стека
        private Item pop() {
            return new Item(); // Извлеченный из стека товар
        }
    }
    Cart cart = new Cart();
    cart.addItem(new Item("Клавиатура", 2000));
    
    // Данная инструкция вызовет ошибку компиляции
    cart.topIndex = 4;

    Наследование и композиция в UML

    hashtag
    Наследование (обобщение)

    Отношение обобщения (generalization) в UML изображается при помощи сплошной линии и жирной треугольной стрелки, ведущей от подкласса с суперклассу.

    Что обозначает это отношение? В спецификации UML по этому поводу сказано

    Обобщение - таксономическое отношение между более общим элементом и более конкретным. Каждый экземпляр конкретного элемента также является непрямым экземпляром обобщенного элемента. Таким образом, конкретизированный элемент косвенно обладает свойствами обобщенного элемента

    Является ли отношение обобщения синонимом наследования (inheritance) в объектно-ориентированном проектировании? Ответ зависит от области применения отношения. Для диаграммы концептуальных классов из модели предметной области ответом будет "нет". В этом случае отношение обобщения подразумевает то, что суперкласс является множеством, а подкласс - подмножеством. С другой стороны, на диаграмме классов проектирования это отношение подразумевает объектно-ориентированное наследование свойств подкласса от суперкласса.

    hashtag
    Композиция

    Композиция (composition), так же известная как композитная агрегация (composite aggregation), является строго определенным типом связи "целое-часть" и полезна в некоторых моделях. Отношение композиции предполагает, что

    1. экземпляр части (например, Wheel) в каждый момент времени принадлежит только одному целому предмету (например, Car);

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

    3. целое ответственно за создание и удаление своих частей - либо через самостоятельное создание\удаление, либо через взаимодействие с другими объектами. Следствием этих ограничений является то, что при уничтожении композитного объекта его части должны быть либо уничтожены, либо присоединены в другому композитному объекту. Например, если реальная настольная игра "Монополия" уничтожается, то также уничтожаются все ее клетки (с концептуальной точки зрения). Аналогично, если программный объект

    Для обозначения композиции в UML используется закрашенный ромб на линии ассоциации со стороны целого.

    class Vehicle {}
    class Car extends Vehicle {}
    Car
    уничтожается, то уничтожаются и программные объекты
    Wheel
    .
    class Car {
        private Wheel[] wheels;
    
        public Car() {
            this.wheels = new Wheel[4];
        }
    }
    
    class Wheel {}

    Лабораторная работа 6

    Задание на лабораторную работу

    hashtag
    Задания для выполнения:

    hashtag
    1. Перегрузка методов

    Вернитесь к классу TimeSpan из второй лабораторной работы.

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

    • конструктор без аргументов (в этом случае временной интервал равен 0 часов и 0 минут);

    • конструктор с 1 аргументом (минуты);

    • конструктор с 2 аргументами (часы и минуты);

    hashtag
    2. Цепочка наследования. В данном задании необходимо выполнить следующие действия:

    1. Создайте класс Person, который должен содержать конструктор, геттеры\сеттеры и следующие поля:

    • фамилия;

    • имя;

    • возраст.

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

    2. Создайте класс Student, который должен наследоваться от класса Person. Добавьте конструктор, геттеры\сеттеры и следующие поля:

    • группа;

    • номер студенческого билета.

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

    3. Создайте класс Lecturer, который должен наследоваться от класса Person. Добавьте конструктор, геттеры\сеттеры и следующие поля:

    • кафедра;

    • зарплата.

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

    4. Используя восходящее преобразование, создайте в классе Main несколько объектов классов Student и Lecturer, после чего создайте массив, который бы мог включать объекты классов Person, Student, Lecturer. Заполните массив объектами этих классов.

    5. Используя цикл, обратитесь к элементам массива и выведите в консоль, с помощью метода toString(), информацию от каждого объекта.

    hashtag
    3. Задание на простой полиморфизм

    В данной задаче мы допишем игру "Камень Ножницы Бяумага".

    1. Скачайте, распакуйте и откройте проект в IntelliJ IDEA, запустите приложение и изучите исходный код;

    2. с помощью механизма полиморфизма создайте классы игровых фигур (Камень, Ножницы, Бумага), которые наследуются от GameShape;

    3. Напишите логику метода generateShape(), который возвращает одну из трех случайных фигур (объект одного из трех классов);

    hashtag
    4. Модификация программы Draw

    Скачайте архив с проектом

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

    hashtag
    Задание состоит в следующем:

    1. Откройте проект, прочитайте комментарии, разберитесь как работает приложение;

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

    3. Задание на дополнительный балл. Добавьте в программу кнопку Clear, по нажатию на которую программа должна стирать все фигуры.

    circle-exclamation

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

    конструктор с 1 аргументом типа TimeSpan (из входящего TimeSpan считываются часы и минуты для создания нового TimeSpan);
  • метод добавления времени ко временному интервалу с 2 аргументами (часы и минуты);

  • метод добавления времени ко временному интервалу с 1 аргументом (минуты);

  • метод добавления времени ко временному интервалу с 1 аргументом типа TimeSpan (из входящего TimeSpan считываются часы и минуты для добавления к текущему);

  • метод вычитания времени из текущего временного интервала с 2 аргументами (часы и минуты);

  • метод вычитания времени из текущего временного интервала с 1 аргументом (минуты);

  • метод вычитания времени из текущего временного интервала с 1 аргументом типа TimeSpan (из входящего TimeSpan считываются часы и минуты для вычитания из текущего).

  • в методе actionPerformed() допишите создание объекта игровой фигуры в зависимости от того, на какую кнопку нажал пользователь (читайте комментарии внутри метода);

  • реализуйте метод checkWinner(), который принимает на вход два объекта игровых фигур - фигура компьютера и игрока. С помощью оператора instanceof выясните, фактический объект какого класса скрывается за ссылочной переменной типа GameShape. В зависимости от сочетания фигур, метод возвращает: 1 если выиграл игрок, -1 если выиграл компьютер или 0 если ничья;

  • Сыграйте в игру и убедитесь, что ваш код корректен и игра работает правильно.

  • file-download
    14KB
    roshambo.rar
    arrow-up-right-from-squareOpen
    Архив с игрой
    file-download
    35KB
    Draw.rar
    arrow-up-right-from-squareOpen
    Архив с проектом
    Человек <фамилия> <имя>, возраст: <возраст>
    Студент группы <группа>, <фамилия> <имя>, возраст: <возраст>. Номер студенческого билета: <номер>
    Преподаватель кафедры <кафедра>, <фамилия> <имя>, возраст: <возраст>. Зарплата: <зарплата>

    Задание на лабораторную работу

    hashtag
    Задание 1. Модификация классов

    Добавьте модификаторы доступа к членам классов, разработанных в лабораторной работе 2 (классы TimeSpan, BankAccount и Student).

    circle-exclamation

    Подумайте над тем, какие члены класса сделать приватными, а какие публичными

    hashtag
    Задание 2. Создание UML-диаграмм

    Для модифицированных классов из второй лабораторной работы нарисуйте диаграммы классов UML.

    hashtag
    Задание 3. Класс IntStack

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

    triangle-exclamation

    Нарисуйте класс в диаграмме классов UML

    При реализации стека учитывайте следующее:

    • при реализации класса вам необходимо указать область видимости для полей и методов. Подумайте о том, какие методы необходимо отнести к "интерфейсу", а какие к реализации;

    • размер стека для внешнего пользователя не имеет значения и теоретически неограничен;

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

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

    • При создании объекта стека инициализируйте внутренний статический массив с каким-то изначальным размером;

    • При вставке элементов проверяйте, не выходите ли вы за пределы внутреннего статического массива;

    circle-exclamation

    ВАЖНО! Стек необходимо реализовать с помощью статического целочисленного массива!

    НЕ ИСПОЛЬЗУЙТЕ КОЛЛЕКЦИИ И ПОТОКИ ДАННЫХ, ТОЛЬКО МАССИВЫ

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

    • пользователь может создать новый стек с конструктором без параметров;

    • пользователю доступна команда pop() для извлечения элемента из стека. Метод pop() возвращает извлеченный элемент. При попытке извлечь из пустого стека, метод возвращает 0 или выбрасывает исключение (реализуйте выброс исключения, если вы знаете, как это делать и что такое исключение);

    hashtag
    Задание 4. Создание класса по UML-диаграмме

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

    вы должны написать "реализацию" класса таким образом, чтобы можно моделировать динамическое поведение стека с помощью обычного статического массива;

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

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

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

  • пользователю доступна команда push(), которая добавляет элемент в стек;
  • пользователю доступна команда peek() для просмотра элемента на вершине стека без его исключения.

  • пользователь может получить значение количества элементов в стеке с помощью метода size();

  • пользователь может проверить, пустой ли стек с помощью метода isEmpty();

  • пользователь может очистить стек полностью с помощью метода clear().

  • Язык моделирования UML

    UML – унифицированный язык моделирования (Unified Modeling Language) – это система обозначений, которую можно применять для объектно-ориентированного анализа и проектирования. Его можно использовать для визуализации, спецификации, конструирования и документирования программных систем.

    circle-exclamation

    Ссылка на приложения для построения диаграмм UML - http://staruml.io/downloadarrow-up-right

    Словарь UML включает три вида строительных блоков:

    • Диаграммы.

    • Сущности.

    • Связи.

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

    Диаграмма – это графическое представление набора элементов, чаще всего изображенного в виде связного графа вершин (сущностей) и путей (связей). Язык UML включает 13 видов диаграмм, среди которых на первом месте в списке — диаграмма классов.

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

    Если кто-нибудь подойдет к вам в темном переулке и спросит: «Хотите посмотреть на диаграмму UML?», знайте – скорее всего, речь идет о диаграмме класса. Большинство диаграмм UML, которые я встречал, были диаграммами классов. - Мартин Фаулер

    Графически класс изображается в виде прямоугольника, разделенного на три блока горизонтальными линиями:

    • имя класса;

    • атрибуты (свойства) класса;

    • операции (методы) класса.

    Для атрибутов и операций может быть указан один из трех типов видимости:

    • - private;

    • ~ без модификатора (default);

    • # protected;

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

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

    Атрибут (свойство) – это именованное свойство класса, описывающее диапазон значений, которые может принимать экземпляр атрибута. Класс может иметь любое число атрибутов или не иметь ни одного. В последнем случае блок атрибутов оставляют пустым. Можно уточнить спецификацию атрибута, указав его тип, кратность (если атрибут представляет собой массив некоторых значений) и начальное значение по умолчанию.

    Статические атрибуты класса обозначаются подчеркиванием.

    Операция (метод) – это реализация метода класса. Класс может иметь любое число операций либо не иметь ни одной. Часто вызов операции объекта изменяет его атрибуты. Графически операции представлены в нижнем блоке описания класса. Допускается указание только имен операций. Имя операции, как и имя класса, должно представлять собой текст. Можно специфицировать операцию, устанавливая ее сигнатуру, включающую имя, тип и значение по умолчанию всех параметров, а применительно к функциям – тип возвращаемого значения.

    Абстрактные методы класса обозначаются курсивным шрифтом. Статические методы класса обозначаются подчеркиванием.

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

    Рассмотрим пример класса - его графическое представление и код на языке Java.

    + public.

    Human.java
    public class Human {
    
        private String name;        // private это "-"
        Boolean gender = true;      // default это "~"
        protected long chromosome;  // protected это "#"
        public int age;             // public это "+"
    
        // Статические атрибуты подчеркиваются
        public static long dna;
        
        // Константы можно отобазить как readOnly
        final int SECRET = 924;
    
        /* Как правило, конструкторы
         * изображаются как обычные методы */
        public Human() {}
        public Human (String name) {this.name = name;}
    
        /* Методы отображаются как
         * [-~#+]имя(тип_аргументов): возвращаемый тип
         * Например: public String foo (int a, double b)
         * будет +foo(int, double): String */
        public void breath() {}
        private void sleep(int hours) {}
        protected boolean sneeze() { return true; }
        int run (int speed, String direction) { return 0; }
        public static int calculateAge() { return 0; }
    }

    Лабораторные работы

    Образец протокола лабораторной работы:

    file-download
    32KB
    образец_протокола.docx
    arrow-up-right-from-squareOpen

    в формате pdf

    file-pdf
    180KB
    образец_протокола.pdf
    PDF
    arrow-up-right-from-squareOpen

    hashtag
    Критерии оценки лабораторной работы (начиная с 3 лабораторной работы)

    Корректно работающая лабораторная работа оценивается в 60 баллов, кроме оценки D за протокол лабораторной работы или в случае списывания лабораторной работы.

    Студент получает дополнительно:

    • до 20 баллов за стилизацию исходного кода;

    • до 10 баллов за оформление протокола лабораторной работы;

    • до 10 баллов за качество кода.

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

    0-5 баллов

    Циклы избыточно сложны, очень сложные условия ветвлений, инициализация переменных выполнена в стиле языка C, неиспользуемые переменные.

    2-4 балла

    D

    Правила грамотного написания кода не соблюдены. Отступы, названия, стилизация не соответствуют современным правилам оформления кода.

    0-9 баллов

    В протоколе некорректно указан номер группы и(-или) студент. Код нечитабелен, пояснения отсутствуют, выводы отсутствуют. Протокол отсутствует.

    Лабораторная не принимается.

    Не выдержана структура класса, некорректные ветвления и циклы.

    0-1 балл

    Оценка

    Стилизация исходного кода (до 20 баллов)

    Оформление протокола (до 10 баллов)

    Качество кода (до 10 баллов)

    A

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

    18-20 баллов

    Оформление без видимых замечаний, присутствуют осмысленные пояснения к коду. Код вставлен текстом с соблюдением отступов и моноширинным шрифтом. Присутствуют выводы к лабораторной работе.

    8-10 баллов

    Циклы, ветвления грамотно оформлены. Инициализация переменных выполнена грамотно. Выдержана правильная структура класса.

    8-10 баллов

    B

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

    15-17 баллов

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

    6-7 баллов

    Неоптимальная форма циклов (отступление от каноничной формы без видимой причины) и ветвлений (пустые блоки).

    5-7 баллов

    C

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

    10-14 баллов

    Лабораторная работа 5

    Задание на лабораторную работу

    circle-exclamation

    В данной лабораторной работе задания выполняются с помощью плагина EduTools!

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

    file-archive
    6KB
    Java_lab_4.zip
    archive
    arrow-up-right-from-squareOpen

    Если вы обновили IntelliJ IDEA, то используйте данный архив для выполнения лабораторной работы

    file-archive
    32KB
    Java_lab_4.zip
    archive
    arrow-up-right-from-squareOpen
    lab4_updated_for_2021.2.2
    circle-exclamation

    Реализуйте каждую задачу с помощью механизма наследования, а потом - с помощью механизма композиции.

    Для каждой реализации нарисуйте UML-диаграмму классов. Вы можете нарисовать одну диаграмму для двух заданий, реализованных с помощью наследования и одну диаграмму для заданий, реализованных с помощью механизма наследования.

    hashtag
    1. Класс Point3D

    Дан класс Point, который моделирует точку в двумерном пространстве. Класс включает в себя следующие конструкторы и публичные методы:

    Создайте класс Point3D, который расширяет класс Point через наследование. Он должен вести себя как Point, за исключением того что это должна быть точка в трехмерном пространстве, которая хранит значение координаты Z.

    Вы должны предоставить те же методы, что и суперкласс, а также реализовать дополнительное поведение

    Класс Point3D() должен переопределить требуемые методы, чтобы они работали корректно с учетом третьей координаты. Также класс Point3D должен вести себя иначе в следующих ситуациях:

    • при вызове метода setLocation(int x, int y), координата z должна быть выставлена в 0;

    • при вызове метода toString(), строка должна выводить три координаты, а не две;

    hashtag
    2. Класс DiscountBill

    Дан класс GroceryBill, который моделирует чек и хранит список товаров, который покупает человек в супермаркете. Класс включает в себя следующие конструкторы и публичные методы:

    Объект GroceryBill взаимодействует с объектами класса Item. Класс Item включает следующие публичные методы:

    К примеру, товар стоит 1.35, а размер скидки 0.25 для постоянных покупателей. Это означает, что постоянный покупатель должен заплатить 1.10. Некоторые товары могут не иметь скидки (размер скидки 0.0). В классе GroceryBill не предусмотрена логика для учета скидки, то есть учитывается только полная стоимость товара.

    Разработайте класс DiscountBill, который расширяет класс GroceryBill и добавляет логику для учета скидки для постоянных клиентов. Конструктор класс DiscountBill должен принимать на вход параметр, который указывает, является ли клиент постоянным.

    Класс DiscountBill должен реализовывать собственную логику метода getTotal() для постоянных покупателей. Например, если полная сумма равна 80 гривен, а скидка для постоянного клиента составила 20 гривен, метод должен возвращать 60 гривен.

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

    Помимо переопределенных методов, класс DiscountBill должен иметь следующие конструкторы и публичные методы:

    Если покупатель не является регулярным, класс DiscountBill должен вести себя как будто общая скидка равна 0 и все товары учтены по их полной стоимости.

    Лабораторная работа 3

    hashtag
    Тема: Инкапсуляция. Знакомство с языком моделирования UML

    hashtag
    Цель лабораторной работы:

    Задание на лабораторную работу

    hashtag
    1. Класс TimeSpan

    hashtag
    1.1 Создание класса

    Создайте класс с названием

    изучить принцип инкапсуляции и его реализацию в языке Java;

  • ознакомиться с базовой нотацией диаграммы классов UML;

  • научиться представлять классы в виде нотации диаграммы классов UML;

  • научиться "читать" диаграмму и писать код исходя из диаграммы классов UML.

  • Возвращает строку в виде "(x,y)"

    public double distance(Point p)

    Возвращает расстояние от текущей точки до входной точки по формуле расстояния Евклида

    public double distanceFromOrigin()

    Возвращает расстояние от начала координат (0, 0) до точки по формуле расстояния Евклида

    метод distanceFromOrigin() должны учитывать координату z и возвращать расстояние по формуле (x1−x2)2+(y1−y2)2+(z1−z2)2\sqrt{(x_1-x_2)^2 + (y_1-y_2)^2 + (z_1-z_2)^2}(x1​−x2​)2+(y1​−y2​)2+(z1​−z2​)2​ .

    Сигнатура

    Описание

    public Point()

    Создает точку с координатами (0, 0)

    public Point(int x, int y)

    Создает точку с координатами (x, y)

    public void setLocation(int x, int y)

    Устанавливает новые координаты точки

    public int getX()

    Возвращает значение координаты X

    public int getY()

    Возвращает значение координаты Y

    Сигнатура

    Описание

    public Point3D()

    Создает точку с координатами (0, 0, 0)

    public Point3D(int x, int y, int z)

    Создает точку с координатами (x, y, z)

    public void setLocation(int x, int y, int z)

    Устанавливает новые координаты

    public double distance(Point3D p)

    Возвращает расстояние от текущей точки до входной точки по формуле расстояния Евклида с учетом координаты z

    public int getZ()

    Возвращает координату Z

    Сигнатура

    Описание

    public GroceryBill(Employee clerk)

    Создает объект GroceryBill для данного clerk

    public void add(Item i)

    Добавляет товар в чек

    public double getTotal()

    Возвращает итоговую стоимость товаров

    public void printReceipt()

    Распечатывает список товаров

    Сигнатура

    Описание

    public double getPrice()

    Возвращает стоимость товара

    public double getDiscount()

    Возвращает скидку для этого товара

    Сигнатура

    Описание

    public DiscountBill (Employee clerk, boolean regularCustomer)

    Создает объект DiscountBill для данного clerk

    public int getDiscountCount()

    Возвращает количество товаров со скидкой

    public double getDiscountAmount()

    Возвращает общую скидку в гривнах

    public double getDiscountPercent()

    Возвращает процент скидки для товаров (на сколько процентов покупатель заплатил меньше).

    Процент можно посчитать по формуле:

    100 - (цена со скидкой * 100) / полная цена

    public String toString()

    TimeSpan
    . Объект класса
    TimeSpan
    хранит интервал времени в часах и минутах (к примеру, временной интервал между 8:00 и 10:30 это 2 часа 30 минут). Класс
    TimeSpan
    должен иметь следующие методы

    Название метода

    Описание метода

    TimeSpan(hours, minutes)

    Конструктор. Входные аргументы – количество часов и минут

    getHours()

    Возвращает целое количество часов во временном интервале, без учета количества минут (например, если интервал 2 часа 15 минут, то метод вернет 2, так как 15 минут мы не учитываем)

    getMinutes()

    Возвращает количество минут во временном интервале, без учета количества часов (например, если интервал 2 часа 15 минут, то метод вернет 15, так как 2 часа мы отбрасываем)

    add(hours, minutes)

    Добавляет указанное количество часов и минут к промежутку. Новое значение промежутка должно пересчитываться корректно. Например, 2 часа 15 минут + 1 час 45 минут = 4 часа 0 минут. Помните, что необходимо проверить корректность входных аргументов (количество часов - не отрицательное число, количество минут от 0 до 59).

    addTimeSpan(timespan)

    Добавляет входной промежуток времени к указанному промежутку (используйте методы getHours() и getMinutes() для получения значений часов и минут).

    Вы должны продумать:

    • как хранить значение временного интервала;

    • типы входных аргументов методов;

    • корректно реализовать требуемые методы;

    • в методах предусмотреть проверки на корректность входных данных.

    hashtag
    1.2. Добавление метода subtract()

    Добавьте к написанному ранее классу метод subtract()со следующей сигнатурой

    Метод вычитает из текущего временного интервала входной временной интервал. Если входной интервал больше текущего, выйдите из метода и не модифицируйте текущий интервал (можете выбросить исключение IllegalArgumentException, если знаете что это и знаете как выбрить исключение).

    hashtag
    1.3 Добавление метода scale()

    Добавьте к написанному ранее классу метод subtract()со следующей сигнатурой

    Метод увеличивает текущий интервал в factorчисло раз. Например, если текущий временной интервал равен 1 час 45 минут, а входной аргумент равен 2, то интервал увеличится до 3 часов 30 минут. Убедитесь, что factor- неотрицательное число, помните про пересчет минут в часы при увеличении интервала.

    hashtag
    2. Класс BankAccount

    hashtag
    2.1 Добавление поля transactionFee

    В программе существует класс BankAccount, исходный код которого приведен ниже.

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

    • добавьте поле transactionFeeтипа double, которое хранит сумму, которая вычитается из баланса каждый раз, когда клиент банка снимает деньги (метод withdraw()). Изначальное значение равно 0.00, но значение может быть изменено в дальнейшем. Производите вычитание суммы каждый раз, когда клиент осуществляет снятие денег;

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

    • в методe deposit() входной аргумент не должен быть ноль или меньше;

    • в методе withdraw() входной аргумент не должно быть ноль или меньше.

    Модификация класса может потребовать создания новых методов и полей.

    hashtag
    2.2 Добавление метода transfer()

    Добавьте к модифицированному ранее классу BankAccoutметод transfer() со следующей сигнатурой

    Метод transfer() осуществляет перевод денег из одного счета (текущего) на другой счет (объект receiver). Первый параметр - счет получателя денег, второй параметр - количество денег, которые переводятся со счета на счет.

    Важно!

    При снятии денег должна учитываться комиссия (поле transactionFee). Следовательно, из текущего объекта должна быть вычтено amount + transactionFee.

    Метод должен модифицировать баланс двух объектов. У текущего объекта должна вычтена сумма amount + transactionFeeиз баланса, а у объекта receiverдолжен быть пополнен баланс счета на сумму amount.

    При реализации метода убедитесь, что amount > 0.

    Если у текущего объекта баланс меньше amount + transactionFee, то выйдите из метода без какого-либо перевода денег.

    hashtag
    3. Класс Student

    Создайте класс Student, который хранит информацию о студенте. У студента есть имя, год обучения (от 1 до 4), а также массив изучаемых дисциплин.

    Класс Student должен иметь следующие методы

    Название метода

    Описание метода

    Student(name, year)

    Конструктор. Входные аргументы – имя и год обучения

    addCourse(courseName)

    Добавляет дисциплину(в формате String) к массиву дисциплин студента

    dropAll()

    Удаляет все дисциплины студента

    getCourseCount()

    Возвращает количество дисциплин, которые студент изучает

    getName()

    Возвращает имя студента

    Вы должны продумать:

    • как хранить данные студенты (типы данных, структуры и так далее);

    • типы входных аргументов методов и тип возвращаемого значения;

    • корректно реализовать требуемые методы;

    • в методах предусмотреть проверки на корректность входных данных.

    public void subtract(TimeSpan span)
    public void scale(int factor)
    // Каждый объект класса BankAccount представляет данные одного
    // счета пользователя, включая имя и баланс счета
    
    public class BankAccount {
        String name;
        double balance;
    
        public void deposit(double amount) {
            balance = balance + amount;
        }
    
        public double getBalance() {
            return this.balance;
        }
    
        public boolean withdraw(double amount) {
            balance = balance - amount;
            return true;
        }
    }
    public boolean transfer(BankAccount receiver, double amount)

    getTotalHours()

    Возвращает количество часов в промежутке в виде дробного числа. Например, если временной интервал 9 часов 45 минут, то метод должен вернуть 9.75.

    getTotalMinutes()

    Возвращает количество минут в текущем промежутке времени (помните, что в интервале есть еще показатель часов, который нужно умножить на 60)

    getTuition()

    Возвращает количество денег, которые студент заплатил за обучение (с условием, что каждый год обучения обошелся студенту в 1000 гривен)

    getYear()

    Возвращает год обучения студента (от 1 до 4)

    (x1−x2)2+(y1−y2)2 \sqrt{(x_1-x_2)^2 + (y_1-y_2)^2}(x1​−x2​)2+(y1​−y2​)2​

    Лабораторная работа 1

    В данной лабораторной работе мы установим необходимое программное обеспечение и познакомимся с базовым синтаксисом и операторами языка Java.

    hashtag
    Тема: Установка программного обеспечения. Базовый синтаксис Java.

    hashtag
    Цели лабораторной работы:

    • установить программное обеспечение для программирования на языке Java;

    • изучить базовый синтаксис языка Java и сравнить его с языком C;

    • реализовать несколько тестовых задач на языке Java.

    Лабораторная работа 4

    hashtag
    Тема: Повторное использование кода. Наследование и композиция

    hashtag
    Цель лабораторной работы:

    • изучить принцип наследования;

    • изучить принцип композиции;

    • разобраться в чем отличие наследования от композиции и когда следует применять тот или иной принцип;

    • научиться отображать композицию и наследование в диаграмме классов UML.

    Абстрактные классы и интерфейсы в UML

    hashtag
    Абстрактный класс

    Абстрактный класс - это класс, который нельзя реализовать непосредственно. Вместо этого создается экземпляр подкласса. Обычно абстрактный класс имеет одну или более абстрактных операций. У абстрактной операции нет реализации, это чистое объявление, которые клиенты могут привязать к абстрактному классу.

    Наиболее распространенным способом обозначения абстрактного класса или операции в UML является написание их имен курсивом. Можно также сделать свойства абстрактными, определяя абстрактное свойство или методы доступа. Если курсив сложно изобразить (например, если вы рисуете на доске), можно прибегнуть к метке {abstract}.

    Для указания класса как абстрактного, выделите класс и выделите чекбокс isAbstract.

    Для указания метода как абстрактного, выделите метод и выделите чекбокс isAbstract.

    hashtag
    Интерфейс

    Интерфейс - это класс, не имеющий реализации, то есть вся его функциональность абстрактна. Интерфейсы прямо соответствуют интерфейсам в Java или C# и являются общей идиомой в других типизированных языках. Интерфейс обозначается стереотипом (ключевым словом) <<interface>>.

    hashtag
    Обозначение интерфейса в StarUML

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

    Для того, чтобы преобразовать его в классический интерфейс согласно стандарту UML, выделите интерфейс и в панели Editors выберите Format -> Label.

    После этого снимите выделение с пункта "Suppress Operations"

    hashtag
    Пример использования абстрактного класса и интерфейса

    Графическое представление в формате UML

    Исходный код

    interface MyInterface{
        void interfaceMethod();
    }
    
    abstract class MyAbstractClass implements MyInterface {
        abstract void abstractMethod();
    }
    
    class MyClass extends MyAbstractClass {
    
        @Override
        void abstractMethod() {}
    
        @Override
        public void interfaceMethod() {}
    }

    Задание на лабораторную работу

    triangle-exclamation

    Нарисуйте диаграммы классов UML для задания 1 по абстрактным классам и для заданий 1, 2, 4 по интерфейсам.

    hashtag
    Абстрактные классы

    hashtag
    Задание 1.

    Дан следующий абстрактный класс

    Создайте класс Dog как подкласс Animal и реализуйте метод makeSound(). Создайте класс Cat как подкласс Animal и реализуйте метод makeSound(). Создайте в методе main() объекты каждого из подклассов и вызовите метод makeSound().

    hashtag
    Задание 2.

    Напишите исходный код классов, приведенных в диаграмме классов UML (методы реализовывать не нужно).

    hashtag
    Интерфейсы

    hashtag
    Задание 1.

    Напишите исходный код класса Point, который реализует следующий интерфейс

    hashtag
    Задание 2.

    Напишите исходный код класса Smartphone, который реализует следующие интерфейсы

    hashtag
    Задание 3.

    Напишите исходный код интерфейса, приведенного в диаграмме классов UML. Напишите исходный код класса DynamicIntArrayImpl, реализующего этот интерфейс (методы реализовывать не нужно)

    circle-check

    Задание на дополнительные баллы. Реализуйте методы класса DynamicIntArrayImpl, чтобы получился рабочий класс. Проверьте корректность его работы.

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

    При удалении элемента, массив должен "сужаться", размер массива уменьшается.

    hashtag
    Задание 4.

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

    hashtag
    Задание 5.

    Дан следующий класс

    Сделайте так, чтобы класс реализовывал интерфейс Comparable. Напишите исходный код метода compareTo(). Логика метода следующая:

    • сначала сравнивается цена автомобилей, "больше" тот автомобиль, у которого меньше цена;

    • если цены равны, то сравнивается год выпуска автомобиля, "больше" тот автомобиль, который меньше по возрасте;

    • если возраст одинаковый, то автомобили сравниваются по количеству лошадиных сил. "Больше" тот автомобиль, у которого лошадиных сил больше.

    triangle-exclamation

    Не используйте типизированный Comparable! Для сравнения используйте только арифметические операторы!

    hashtag
    Задание на дополнительные баллы. Приложение SortingList

    Скачайте архив с проектом по этому адресу - .

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

    В программе уже реализована сортировка студентов по имени.

    Задание состоит в следующем:

    • доделать программу, чтобы по нажатию на соответствующие кнопки происходила сортировка студентов по фамилии и по среднему баллу;

    • задание на дополнительные баллы - доработайте программу так, чтобы при повторном нажатии на ту же кнопку сортировки, осуществлялась обратная сортировка студентов. То есть, при первом нажатии на кнопку "Сортировка по имени" студенты сортировались в прямом порядке (по возрастанию), а при повторном нажатии на эту же кнопку, студенты сортировались в обратном порядке (по убыванию).

    circle-exclamation

    Список студентов находится в коллекции students, для сортировки необходимо вызвать у коллекции метод sort() и в этот метод передать объект компаратора.

    Что такое компаратор. Базовая операция для почти любой сортировки – сравнение двух элементов. Если вы сортируете обычные числа или строки, сравнение происходит элементарно по известным всем правилам. Но как быть, если вы хотите отсортировать объекты, которые могут иметь десятки полей различных типов?

    Для этого существует так называемый компаратор. Компаратор в Java – это объект класса, который реализует интерфейс Comparator. Интерфейс определяет всего один метод compare(), который принимает на вход два объекта типаObject. Если первый объект «меньше» – метод возвращает отрицательное число (обычно это просто -1, но может быть и любое другое отрицательное число), если первый объект «больше» – метод возвращает положительное число (обычно это 1, но может быть и любое другое положительное число), если объекты «равны» – метод возвращает 0.

    Задача программиста - прописать нужную логику сравнения и вернуть -1/0/1 в том или ином случае.

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

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

    В данном случае мы сравниваем два поля name у двух объектов класса Student. Так как это тип String, нам нет нужды сравнивать строки «вручную», мы просто можем воспользоваться методом compareTo(), который есть у любого объекта класса String.

    ссылкаarrow-up-right
    abstract class Animal {
    
        private String name;
    
        public Animal(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        abstract String makeSound();
    }
    interface Movable {
        void setX(double x);
        void setY(double y);
        double getX();
        double getY();
    
        void moveTo(double x, double y);
    }
    interface GPS {
        double[] getCoordinates();
    }
    
    interface Cellular {
        void makeCall();
        void receiveCall();
    }
    class Car {
    
        private int price;
        private int year; // ГОД ВЫПУСКА
        private int horsePower;
    
        // ДОБАВЬТЕ КОНСТРУКТОР С 3 АРГУМЕНТАМИ
    
        // ДОБАВЬТЕ ГЕТТЕРЫ И СЕТТЕРЫ
    }
    public class NameSorter implements Comparator {
        @Override
        public int compare(Object o1, Object o2) {
            if (o1 instanceof Student && o2 instanceof Student) {
                Student s1 = (Student) o1;
                Student s2 = (Student) o2;
                return s1.getName().compareTo(s2.getName());
            }
            return 0;
        }
    }

    Наследование и композиция

    hashtag
    Повторное использование кода

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

    • композиция (composition) - объекты уже имеющихся классов просто создаются внутри нового класса. Программист просто использует функциональность уже готового кода;

    • наследование (inheritance) - новый класс создается как специализация уже существующего класса. Взяв существующий класс за основу, вы добавляете к нему свой код без изменения существующего класса.

    Композиция - использование функционала одних объектов в составе других объектов. Рассмотрим пример класса FileManager, в котором определен метод для сохранения текстовых данных в файл. Класс Document использует функционал класса FileManager, чтобы сохранить текстовый документ на жесткий диск.

    hashtag
    Базовые понятия механизма наследования

    Наследование - отношение между классами, в котором один класс повторяет структуру и поведение другого класса (или нескольких других классов).

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

    hashtag
    Создание подкласса в Java

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

    В Java, в отличие от C++, отсутствует множественное наследование, то есть подкласс может создаваться на основе только одного суперкласса.

    В Java присутствует многоуровневое наследование: подкласс может быть суперклассом для другого класса. Благодаря этому можно создавать целые цепочки классов, связанные механизмом наследования

    hashtag
    Наследование членов суперкласса

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

    Закрытыми могут быть как поля класса, так и его методы. Если необходимо открыть поля или методы для доступа к ним со стороны подкласса, при объявлении членов суперкласса используют слово protected либо создают геттеры и сеттеры для доступа к полям. К примеру, данный код скомпилируется и будет работать корректно

    Данный пример демонстрирует доступ к полям с помощью геттеров

    hashtag
    Инициализация базового класса

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

    Однако наследование не просто копирует интерфейс базового класса. Когда вы создаете объект производного класса, внутри него содержится подобъект базового класса. Этот подобъект выглядит точно так же, как выглядел бы созданный обычным порядком объект базового класса. Поэтому извне представляется, будто бы в объекте производного класса "упакован" объект базового класса.

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

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

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

    Как видно из данного примера, цепочка вызовов конструкторов начинается с самого базового класса. Таким образом, подобъект базового класса инициализируется еще до того, как он станет доступным для конструктора производного класса. Даже если конструктор класса Cat не будет определен, Java сгенерирует конструктор по умолчанию, в котором также будет вызван конструктор базового класса.

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

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

    hashtag
    Ключевое слово super

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

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

    Как видно из примера, ключевое слово super имеет что-то общее с ключевым словом this.

    hashtag
    Переопределение методов

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

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

    Как вы уже понимаете, в данной части кода

    был вызван метод getInfo() суперкласса, который выводит информацию только о двух параметрах коробки, тогда как класс Box3D содержит три параметра. Таким образом, метод getInfo() для класса Box3D становится как бы некорректным, неправильным, он выдает неполную информацию об объекте. Как решить эту проблему?

    Первый вариант - создать в подклассе Box3D свой метод для вывода информации

    Тогда мы будем вызывать метод get3DInfo()

    что даст нам корректный результат

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

    Самым правильным вариантом будет как бы "переписать" метод getInfo(), предоставить версию метода getInfo() для класса Box3D. Таким образом и класс Box и класс Box3D будет иметь метод getInfo(), просто в классе Box3D он будет иначе реализован, с учетом появления третьего параметра. Кроме того, объект класса Box3D теперь будет содержать только один метод для получения информации об объекте и этот метод будет корректно работать.

    Для реализации своеобразного "переписывания" метода в подклассе, в Java существует механизм переопределения метода (method overriding).

    Воспользуемся механизмом переопределения метода, чтобы корректно решить проблему с классами Box и Box3D (в примере для наглядности опущены некоторые члены классов)

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

    Также обратите внимание на строку 33, где записано @Override. Такая запись называется аннотацией.

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

    Результат работы нашего примера будет следующим

    Обратите внимание, что и для класса Box и для класса Box3D мы вызываем метод с одним и тем же названием и с одной и той же сигнатурой

    Но в зависимости от того, для какого класса мы вызываем этот метод, в первом случае отрабатывает метод в классе Box, а во втором случае - метод в классе Box3D.

    circle-exclamation

    Следует отличать механизм перегрузки метода от механизма переопределения метода.

    Метод может быть как перегруженным, так и переопределенным.

    hashtag
    Запрет наследования с помощью ключевого слова final

    Иногда бывает необходимо запретить наследоваться от какого-то класса либо запретить переопределять метод. В этом случае, в объявлении класса или метода укажите ключевое слово final

    hashtag
    Класс Object

    В Java определен специальный класс Object, который является суперклассом для всех классов Java. Иными словами, все классы в языке Java являются подклассами, производными от класса Object.

    В классе Object определены перечисленные ниже методы, которые доступны в любом объекте

    С некоторыми методами класса Object мы встретимся позже, а сейчас нам интересен только метод toString().

    hashtag
    Метод toString()

    Метод toString() призван возвращать строковое представление объекта (список значений полей). Рассмотрим пример

    Обратите внимание, что класс Box не содержит никаких членов, однако, так как Box наследуется от класса Object, ему доступны методы суперкласса и метод toString() в частности.

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

    Метод toString() в классе Object выводит полное название класса и 16-ричное представление хеш-кода объекта.

    circle-info

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

    Метод getInfo() в примере выше был использован только в демонстрационных целях!

    Чтобы метод toString() выводил нужную информацию, его необходимо пепреопределить как в следующем примере

    Результат будет следующим

    Также метод toString() имеет одну примечательную особенность - его можно явно не вызывать, а просто указывать ссылочную переменную, Java сама вызовет метод toString(). Например, следующий код

    выдаст следующий результат

    Как вы видите, результат работы строк 5 и 6 является идентичным.

    Возобновляет работу потока, ожидающего уведомления от вызывающего объекта

    void notifyAll()

    Возобновляет работу всех потоков, ожидающих уведомления от вызывающего объекта

    String toString()

    Возвращает символьную строку, описывающую объект

    void wait()

    void wait(long мсек)

    void wait(long мсек, int наносек)

    Ожидает исполнения другого потока

    Метод

    Описание

    Object clone()

    Создает новый объект, аналогичный клонируемому объекту

    boolean equals(Object объект)

    Определяет равнозначность объектов

    void finalize()

    Вызывается перед тем, как неиспользуемый объект будет удален "сборщиком мусора"

    Class<?> getClass()

    Определяет класс объекта во время выполнения

    int hashCode()

    Возвращает хеш-код, связанный с вызывающим объектом

    void notify()

    class FileManager {
        public void saveToFile(String text, String path) {
            // тело метода
        }
    }
    
    class Document {
        
        // класс Document содержит ссылку на объект
        // класса FileManager
        private FileManager manager;
        private StringBuilder contents;
        private String path;
    
        public Document(FileManager manager, String path) {
            this.manager = manager;
            this.contents = new StringBuilder();
            this.path = path;
        }
    
        public void saveDocument() {
            manager.saveToFile(contents.toString(), path);
        }
    }
    // Суперкласс
    class Person {
        String firstName;
        String lastName;
    }
    
    // Подкласс
    class Student extends Person {
        String group;
        long id;
    }
    
    public class Main {
        public static void main(String[] args) {
            Student student = new Student();
            student.firstName = "Иван";
            student.lastName = "Иванов";
            student.id = 10000L;
        }
    }
    // Суперкласс
    class Person {
        String firstName;
        String lastName;
    }
    
    class UniversityMember{}
    
    // МНОЖЕСТВЕННОЕ НАСЛЕДОВАНИЕ ЗАПРЕЩЕНО!
    // ЭТОТ КОД ВЫЗОВЕТ ОШИБКУ КОМПИЛЯТОРА
    class Student extends Person, UniversityMember {
        String group;
        long id;
    }
    class Vehicle {
    
        public void moveTo(Point destination) {
            // тело метода
        }
    }
    
    class Truck extends Vehicle {
    
        public void carryWeight(double weight) {
            // тело метода
        }
    }
    
    class DumpTruck extends Truck {
    
        public void dumpWeight() {
            // тело метода
        }
    }
    class Shape2D {
        private double width;
        private double height;
    }
    
    class Rectangle extends Shape2D {
    
        // ОШИБКА НА ЭТАПЕ КОМПИЛЯЦИИ
        public double getArea() {
            return width * height;
        }
    }
    class Shape2D {
        protected double width;
        protected double height;
    }
    
    class Rectangle extends Shape2D {
    
        // Данный код корректен
        public double getArea() {
            return width * height;
        }
    }
    class Shape2D {
        private double width;
        private double height;
    
        public double getWidth() {
            return width;
        }
    
        public void setWidth(double width) {
            this.width = width;
        }
    
        public double getHeight() {
            return height;
        }
    
        public void setHeight(double height) {
            this.height = height;
        }
    }
    
    class Rectangle extends Shape2D {
    
        // Данный код корректен
        public double getArea() {
            return getWidth() * getHeight();
        }
    }
    class Animal {
        public Animal() {
            System.out.println("Конструктор класса Animal");
        }
    }
    
    class Mammal extends Animal {
        public Mammal() {
            System.out.println("Конструктор класса Mammal");
        }
    }
    
    class Cat extends Mammal {
        public Cat() {
            System.out.println("Конструктор класса Cat");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Cat cat = new Cat();
        }
    }
    Конструктор класса Animal
    Конструктор класса Mammal
    Конструктор класса Cat
    public class Main {
        public static void main(String[] args) {
            Box3D box = new Box3D(100);
        }
    }
    
    class Box {
        public double width;
        public double height;
    
        public Box(double width, double height) {
            this.width = width;
            this.height = height;
        }
    }
    
    class Box3D extends Box {
        public double depth;
    
        // НЕТ ЯВНОГО ВЫЗОВА КОНСТРУКТОРА СУПЕРКЛАССА !
        public Box3D(double depth) {
            this.depth = depth;
        }
    }
    public class Main {
        public static void main(String[] args) {
            Box3D box = new Box3D(100, 200, 300);
        }
    }
    
    class Box {
        public double width;
        public double height;
    
        public Box(double width, double height) {
            this.width = width;
            this.height = height;
        }
    }
    
    class Box3D extends Box {
        public double depth;
        
        public Box3D(double width, double height, double depth) {
            super(width, height); // <-- ВЫЗОВ КОНСТРУКТОРА СУПЕРКЛАССА
            this.depth = depth;
        }
    }
    class Box {
        private double width;
        private double height;
    
        public Box(double width, double height) {
            this.width = width;
            this.height = height;
        }
    
        // Площадь прямоугольника
        public double getArea() {
            return width * height;
        }
    }
    
    class Box3D extends Box {
        private double depth;
    
        public Box3D(double width, double height, double depth) {
            super(width, height); // <-- ВЫЗОВ КОНСТРУКТОРА СУПЕРКЛАССА
            this.depth = depth;
        }
        
        // Мы используем метод суперкласса, чтобы
        // посчитать площадь трехмерной коробки
        public double get3DArea() {
            double area2D = super.getArea(); // <---- Вызов метода суперкласса
            return area2D * depth;
        }
    }
    public class Main {
        public static void main(String[] args) {
            Box3D box = new Box3D(100, 200, 300);
            System.out.println(box.getInfo());
        }
    }
    
    class Box {
        private double width;
        private double height;
    
        public Box(double width, double height) {
            this.width = width;
            this.height = height;
        }
    
        public String getInfo() {
            return "Объект Box {" +
                    "ширина = " + width +
                    ", высота = " + height +
                    '}';
        }
    }
    
    class Box3D extends Box {
        private double depth;
    
        public Box3D(double width, double height, double depth) {
            super(width, height);
            this.depth = depth;
        }
    }
    Объект Box {ширина = 100.0, высота = 200.0}
    Box3D box = new Box3D(100, 200, 300);
    System.out.println(box.getInfo());
    class Box3D extends Box {
        private double depth;
    
        public Box3D(double width, double height, double depth) {
            super(width, height);
            this.depth = depth;
        }
        
        public String get3DInfo() {
            return "Объект Box3D {" +
                    "ширина = " + super.getWidth() +
                    ", высота = " + super.getHeight() +
                    ", глубина = " + depth +
                    '}';
        }
    }
    class Box {
        private double width;
        private double height;
    
        public double getWidth() {
            return width;
        }
    
        public void setWidth(double width) {
            this.width = width;
        }
    
        public double getHeight() {
            return height;
        }
    
        public void setHeight(double height) {
            this.height = height;
        }
    
        public Box(double width, double height) {
            this.width = width;
            this.height = height;
        }
    
        public String getInfo() {
            return "Объект Box {" +
                    "ширина = " + width +
                    ", высота = " + height +
                    '}';
        }
    }
    public class Main {
        public static void main(String[] args) {
            Box3D box = new Box3D(100, 200, 300);
            System.out.println(box.get3DInfo());
        }
    }
    Объект Box3D {ширина = 100.0, высота = 200.0, глубина = 300.0}
    public class Main {
        public static void main(String[] args) {
            
            Box box = new Box(600,600);
            System.out.println(box.getInfo());
            
            Box3D box3D = new Box3D(100, 200, 300);
            System.out.println(box3D.getInfo());
        }
    }
    
    class Box {
        
        // Поля, конструктор и геттеры\сеттеры
    
        public Box(double width, double height) {
            this.width = width;
            this.height = height;
        }
    
        public String getInfo() {
            return "Объект Box {" +
                    "ширина = " + width +
                    ", высота = " + height +
                    '}';
        }
    }
    
    class Box3D extends Box {
    
        // Поля, конструктор и геттеры\сеттеры
        
        @Override
        public String getInfo() {
            return "Объект Box3D {" +
                    "ширина = " + super.getWidth() +
                    ", высота = " + super.getHeight() +
                    ", глубина = " + depth +
                    '}';
        }
    }
    Объект Box {ширина = 600.0, высота = 600.0}
    Объект Box3D {ширина = 100.0, высота = 200.0, глубина = 300.0}
    Box box = new Box(600, 600);
    System.out.println(box.getInfo());
    
    Box3D box3D = new Box3D(100, 200, 300);
    System.out.println(box3D.getInfo());
    final class A {}
    
    class B extends A {
        // ВЫЗОВЕТ ОШИБКУ КОМПИЛЯЦИИ !
    }
    
    class C {
        final public void foo() {}
    }
    
    class D extends C {
        @Override
        public void foo() {} // <-- Ошибка компиляции !
    }
    public class Main {
        public static void main(String[] args) {
            
            Box box = new Box();
            System.out.println(box.toString());
        }
    }
    
    class Box{}
    com.company.Box@1540e19d
    public class Main {
        public static void main(String[] args) {
    
            Box box = new Box(100, 200);
            System.out.println(box.toString());
        }
    }
    
    class Box {
        private double width;
        private double height;
    
        public Box(double width, double height) {
            this.width = width;
            this.height = height;
        }
    
        @Override
        public String toString() {
            return "Box{" +
                    "width=" + width +
                    ", height=" + height +
                    '}';
        }
    }
    Box{width=100.0, height=200.0}
    public class Main {
        public static void main(String[] args) {
    
            Box box = new Box(100, 200);
            System.out.println(box.toString());
            System.out.println(box);
        }
    }
    Box{width=100.0, height=200.0}
    Box{width=100.0, height=200.0}