webprogr.ru Новости

RSS

{{toodo.preview}}

...
{{toodo.name}}

{{toodo.preview}}

далее..

{{toodo.preview}}

{{toodo.name}}

{{toodo.preview}}

смотреть..

toodo.name

{{toodo.preview}}

{{toodo.name}}

{{toodo.preview}}

перейти...

{{toodo.preview}}

...
{{toodo.name}}

{{toodo.preview}}

далее..

{{toodo.preview}}

{{toodo.name}}

{{toodo.preview}}

смотреть..

toodo.name

{{toodo.preview}}

{{toodo.name}}

{{toodo.preview}}

перейти...


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

Лабораторная работа I по дисциплине Современные технологии объектно-ориентированного программирования

Лабораторная 1, делается преимущественно с ИИ, глубокое понимание кода и его частей не требуется.
Нужно уметь ответить на вопросы по теории ООП, а также уметь настраивать минимально необходимое окружение.

 Лабораторная работа №1 Часть 1

Введение в Java и объектно-ориентированное программирование


Предварительные знания

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

  • переменные и типы данных;

  • условия;

  • циклы;

  • функции;

  • массивы;

  • базовые принципы алгоритмизации.

Знание Java не требуется. Предполагается, что студенты ранее работали с Go и знакомы с C и Python.



Цель работы

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

После выполнения работы студент должен понимать:

  • назначение JVM, JDK и JRE;

  • что представляет собой Java bytecode;

  • этапы компиляции и выполнения Java-программы;

  • смысл принципа Write Once, Run Anywhere;

  • структуру простой программы на Java;

  • основные типы данных, переменные, операторы, условия, циклы, массивы и методы;

  • различия процедурного и объектно-ориентированного подходов;

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



1. Подготовка среды разработки

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

  1. JDK — Java Development Kit.

  2. IDE для Java, рекомендуется Visual Studio Code или IntelliJ IDEA Community Edition .

  3. Терминал операционной системы.

Версию JDK могут отличаться, лучше поставить свежую. 

После установки открыть терминал и выполнить:

java -version
 и
javac -version
 

В отчёт записать полученные версии.

Контрольный вопрос

Объясните, почему после установки JDK доступны две разные команды:

 
 
java
javac
 

Необходимо выяснить назначение каждой из них.

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

 JDK, JRE и JVM

Упрощённая учебная схема выглядит следующим образом:

 
 
JDK — Java Development Kit
│
├── инструменты разработки
│   ├── javac — компилятор
│   ├── javap — просмотр структуры и байт-кода классов
│   ├── jar   — создание JAR-архивов
│   └── другие инструменты
│
└── среда выполнения
    ├── JVM
    └── стандартные библиотеки Java


JVM — Java Virtual Machine

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

JRE — Java Runtime Environment

JRE — среда, необходимая для выполнения Java-программ:

  • JVM;

  • стандартные библиотеки;

  • средства запуска приложения.

Исторически JRE распространялась отдельно. В современных версиях Java студенту следует устанавливать именно JDK, поскольку отдельная JRE больше не является основным способом распространения Java со стороны OpenJDK и Oracle. JDK включает инструменты как для разработки, так и для выполнения программы.

JDK — Java Development Kit

JDK — полный комплект разработчика. Он содержит компилятор, средства запуска, отладки, анализа и упаковки приложений.


2. Первая программа без IDE

Создайте папку:

 
 
lab01
 

В ней создайте файл:

 
 
Main.java
 

со следующим содержимым:

 
 
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, Java!");
    }
}
 

Скомпилируйте программу:

 
 
javac Main.java
 

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

Должен появиться новый файл:

 
 
Main.class
 

Запустите программу:

 
 
java Main
 

Получите:

 
 
Hello, Java!
 

Задание 2.1

Ответьте письменно:

  1. Какой файл содержит исходный код?

  2. Какой файл появился после компиляции?

  3. Почему для запуска используется:

 
 
java Main
 

а не

 
 
java Main.class
 
  1. Является ли файл Main.class машинным кодом конкретного процессора?

  2. Какую роль при запуске выполняет JVM?


3. Цепочка выполнения Java-программы

На основании выполненного эксперимента заполните схему:

 
 
Main.java
    ↓
____________
    ↓
Main.class
    ↓
____________
    ↓
выполнение программы
 

Необходимо самостоятельно определить, что находится в пропусках.

В отчёте представить итоговую схему примерно следующего уровня:

 
 
исходный код Java
        ↓
компилятор
        ↓
байт-код
        ↓
JVM
        ↓
выполнение на конкретной системе
 

После этого ответить на вопрос:

Почему один и тот же .class-файл в общем случае может выполняться и в Windows, и в Linux, и в macOS?

На основании ответа сформулировать своими словами смысл:

Write Once, Run Anywhere.


4. Создание проекта в IntelliJ IDEA или Visual Studio Code

Создайте новый Java-проект.

Название проекта:

 
 
JavaLab01
 

Создайте класс:

 
 
Main
 

Запустите его средствами IDE.

Проверьте, что выбран правильный JDK.

В отчёт добавить скриншот, на котором одновременно видны:

  • окно IDE;

  • исходный код;

  • результат выполнения программы;

  • используемая версия JDK.

Подобное создание и запуск проекта непосредственно предусмотрено практическими заданиями РПД.


5. Быстрый вход в синтаксис Java

Студенты уже знакомы с Go, C и Python, поэтому синтаксис Java предлагается изучать не отдельно по каждой конструкции, а через небольшую программу.

Необходимо написать консольную программу расчёта суммы покупки.

Пользователь вводит:

 
 
Название товара
Цена
Количество
 

Программа должна вычислить:

 
 
Стоимость = Цена × Количество
 

Если стоимость превышает 5000, применяется скидка 10%.

Пример:

 
 
Введите название товара:
Ноутбук

Введите цену:
70000

Введите количество:
2

Товар: Ноутбук
Количество: 2
Стоимость без скидки: 140000.0
Скидка: 14000.0
Итого: 126000.0
 

Для ввода использовать:

 
 
Scanner
 

Пример создания объекта:

 
 
Scanner scanner = new Scanner(System.in);
 

6. Что необходимо использовать в программе

В программе должны присутствовать:

Переменные разных типов

 
 
int
double
boolean
String
 

Например:

 
 
int count;
double price;
String productName;
boolean hasDiscount;
 

Арифметические операторы

 
 
+
-
*
/
 

Оператор сравнения

Например:

 
 
total > 5000
 

Условная конструкция

 
 
if
else
 

7. Циклы

Измените программу таким образом, чтобы пользователь мог обработать несколько товаров.

В начале пользователь вводит количество товаров:

 
 
Сколько товаров необходимо ввести?
3
 

После этого используется цикл.

Например:

 
 
for (int i = 0; i < count; i++) {
    ...
}
 

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


8. Массивы

Создайте массив:

 
 
double[] prices;
 

Сохраните в нём цены пяти товаров.

Найдите:

  • сумму цен;

  • минимальную цену;

  • максимальную цену;

  • среднюю цену.

Запрещается использовать готовые функции поиска минимума и максимума.


9. Методы

Вынесите вычисление скидки из метода main().

Создайте метод:

 
 
static double calculateDiscount(double amount)
 

Правила:

 
 
до 5 000       → 0 %
5 000–10 000   → 5 %
более 10 000   → 10 %

 

Пример вызова:

 
 
double discount = calculateDiscount(total);
 

10. Сравнение со знакомыми языками

Заполните небольшую таблицу самостоятельно.

КонструкцияPythonJava
точка входа?public static void main(String[] args)
целое число?int
вещественное число?double
логический тип?boolean
строка?String
вывод?System.out.println()
условие?if (...)
цикл?for (...)
массив?int[]
функция?метод

Здесь не требуется писать длинные определения. Цель — обнаружить, что базовые алгоритмические конструкции студенту уже знакомы, изменяется прежде всего синтаксис и модель организации программы.


11. Процедурный подход

Рассмотрим задачу обработки финансовых операций.

Каждая операция имеет сумму.

В процедурном варианте можно написать:

 
 
public class Main {

    static double calculateCommission(double amount, int type) {
        if (type == 1) {
            return amount * 0.02;
        }

        if (type == 2) {
            return amount * 0.01;
        }

        return 0;
    }

    public static void main(String[] args) {
        double amount = 10000;

        System.out.println(
                calculateCommission(amount, 1)
        );
    }
}
 

Здесь:

 
 
1 — операция по банковской карте
2 — операция через СБП
 

Задание

Ответьте:

  1. Где находятся данные?

  2. Где находится логика работы с данными?

  3. Что произойдёт с методом calculateCommission(), если появятся ещё 10 видов операций?

  4. Почему использование чисел 1, 2, 3 для обозначения типов операций неудобно?


12. Первый объектно-ориентированный вариант

Теперь та же предметная область описывается объектами.

Студентам не требуется пока уметь самостоятельно проектировать такую иерархию. На этом этапе необходимо понять идею.

Создайте файл:

 
 
Transaction.java
 
 
 
public abstract class Transaction {

    private double amount;

    public Transaction(double amount) {
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }

    public abstract double calculateCommission();
}
 

Создайте:

 
 
CardTransaction.java
 
 
 
public class CardTransaction extends Transaction {

    public CardTransaction(double amount) {
        super(amount);
    }

    @Override
    public double calculateCommission() {
        return getAmount() * 0.02;
    }
}
 

Создайте:

 
 
SbpTransaction.java
 
 
 
public class SbpTransaction extends Transaction {

    public SbpTransaction(double amount) {
        super(amount);
    }

    @Override
    public double calculateCommission() {
        return getAmount() * 0.01;
    }
}
 

И измените Main:

 
 
public class Main {

    public static void main(String[] args) {

        Transaction[] transactions = {
                new CardTransaction(10000),
                new SbpTransaction(10000)
        };

        for (Transaction transaction : transactions) {
            System.out.println(
                    transaction.calculateCommission()
            );
        }
    }
}
 

Запустите программу.


13. Найдите четыре принципа ООП

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

1. Абстракция

Ответьте:

Что общего мы выделили у всех финансовых операций?

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


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

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

 
 
private double amount;
 

Ответьте:

  • почему поле сделано private;

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

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


3. Наследование

Найдите конструкции:

 
 
extends Transaction
 

Ответьте:

Какие свойства и поведение классы CardTransaction и SbpTransaction получают от Transaction?


4. Полиморфизм

Обратите внимание:

 
 
Transaction[] transactions
 

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

При выполнении:

 
 
transaction.calculateCommission();
 

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

Ответьте:

Почему для CardTransaction вызывается одна версия calculateCommission(), а для SbpTransaction — другая, хотя переменная имеет тип Transaction?


14. Небольшое самостоятельное изменение

Добавьте новый вид операции:

 
 
CashTransaction
 

Комиссия:

 
 
0 %
 

Требования:

  1. Создать новый класс.

  2. Наследовать его от Transaction.

  3. Реализовать calculateCommission().

  4. Добавить объект нового класса в массив:

 
 
Transaction[] transactions
 

Важный вопрос

После добавления CashTransaction потребовалось ли изменять цикл:

 
 
for (Transaction transaction : transactions) {
    System.out.println(transaction.calculateCommission());
}
 

Если нет — объясните, какое свойство ООП позволило этого добиться.


15. JVM, JDK и JRE

По результатам работы составьте схему связи понятий:

 
 
JDK
JRE
JVM
 

И заполните таблицу.

ПонятиеНазначение
JVM 
JRE 
JDK 

Дополнительно объясните назначение:

 
 
javac
java
 

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

 
 
разработчик
    ↓
JDK
    ↓
javac
    ↓
bytecode
    ↓
JVM
    ↓
выполнение
 

Именно знание синтаксиса Java, основ стандартной библиотеки и принципов работы с JDK/IDE входит в результаты обучения по ПКН-2 данной дисциплины.


16. Контрольные вопросы

При защите студент должен ответить на вопросы.

  1. Чем процедурный подход отличается от объектно-ориентированного?

  2. Что такое объект?

  3. Что такое класс?

  4. Что означает абстракция?

  5. Что означает инкапсуляция?

  6. Что означает наследование?

  7. Что означает полиморфизм?

  8. Что представляет собой JVM?

  9. Для чего нужен JDK?

  10. Что понимается под JRE?

  11. Чем javac отличается от java?

  12. Что представляет собой Java bytecode?

  13. Почему Java обычно не компилируется непосредственно в инструкции конкретного процессора?

  14. Как выполняется программа после запуска команды java Main?

  15. Что означает принцип Write Once, Run Anywhere?

  16. Почему наличие JVM позволяет запускать один байт-код на разных операционных системах?

  17. Что находится в файле .java?

  18. Что находится в файле .class?

  19. Для чего нужен метод:

 
 
public static void main(String[] args)
 
  1. Какие базовые типы Java использовались в работе?

  2. Чем метод в Java похож на функцию в C, Go или Python?

  3. Что делает ключевое слово new?

  4. Для чего используется private?

  5. Что делает extends?

  6. Что означает аннотация @Override в рассмотренном примере?


17. Что должно быть представлено преподавателю

Студент предоставляет:

  1. Проект JavaLab01.

  2. Исходный код выполненных заданий.

  3. Скриншот проверки:

 
 
java -version
javac -version
 
  1. Скриншот программы, запущенной из IntelliJ IDEA или VS Code

  2. Результат выполнения процедурной версии.

  3. Результат выполнения объектно-ориентированной версии.

  4. Реализованный CashTransaction.

  5. Краткий отчёт.


18. Структура отчёта

1. Титульный лист

2. Цель работы

3. Используемое программное обеспечение

Указать:

 
 
ОС:
IDE:
версия JDK:
 

4. Ход выполнения

Кратко показать:

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

5. Схема выполнения Java-программы

 
 
.java → javac → .class → JVM → выполнение
 

6. Реализованный код

Не обязательно помещать в отчёт весь проект — достаточно ключевых фрагментов.

7. Ответы на вопросы

Обязательно раскрыть:

  • JVM;

  • JDK;

  • JRE;

  • bytecode;

  • WORA;

  • процедурный подход;

  • ООП;

  • абстракцию;

  • инкапсуляцию;

  • наследование;

  • полиморфизм.

8. Вывод

Примерная постановка:

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

Текст вывода студент формулирует самостоятельно.


 

То есть логика первой ЛР части 1 получается:

 
 
Установили Java
        ↓
увидели java и javac
        ↓
.java → .class
        ↓
поняли JVM и bytecode
        ↓
быстро перенесли знакомые
if / for / массивы / функции
на синтаксис Java
        ↓
решили задачу процедурно
        ↓
увидели ту же задачу в ООП
        ↓
нашли в реальном коде
абстракцию
инкапсуляцию
наследование
полиморфизм
 



Лабораторная работа № 1 Часть 2

Синтаксис Java

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

Задача данного этапа — научиться выражать уже знакомые алгоритмические конструкции средствами Java.

Краткий ориентир

ЭлементJava
целое числоint
вещественное числоdouble
логический типboolean
символchar
строкаString
условиеif / else
множественный выборswitch
циклfor, while, do while
массивint[], double[]
выводSystem.out.println()
методstatic returnType name(...)

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


Практическое задание «Обработка платежей»

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

Для каждой операции задаются:

  • сумма;

  • способ оплаты.

Используются три способа оплаты:

 
 
1 — банковская карта
2 — СБП
3 — наличные
 

Комиссия определяется следующим образом:

Способ оплатыКомиссия
Банковская карта2 %
СБП1 %
Наличные0 %

Программа должна запросить количество операций N, после чего обработать N платежей.

Пример работы

 
 
Количество платежей: 3

Платеж 1
Сумма: 10000
Способ оплаты: 1

Платеж 2
Сумма: 2500
Способ оплаты: 2

Платеж 3
Сумма: 5000
Способ оплаты: 3

Общая сумма платежей: 17500.00
Общая комиссия: 225.00
Средняя сумма платежа: 5833.33

 

Требования к процедурной реализации

На первом этапе задача должна быть решена без создания собственной объектной модели платежей.

В программе необходимо использовать:

  • int;

  • double;

  • boolean;

  • String;

  • арифметические операции;

  • операторы сравнения;

  • if/else или switch;

  • цикл;

  • массив;

  • отдельный метод;

  • консольный ввод/вывод.

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

 
 
static double calculateCommission(double amount, int paymentType) {
    // реализация студента
}
 

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

 
 
double[] amounts;
 

Программа должна дополнительно определить:

  • максимальный платеж;

  • минимальный платеж;

  • средний платеж;

  • общую сумму платежей;

  • общую сумму комиссии.

Если пользователь ввел сумму <= 0, операция считается некорректной.


Анализ процедурного решения

После выполнения программы ответьте:

  1. Где в программе хранятся данные платежа?

  2. Где находится логика вычисления комиссии?

  3. Что определяет число 1, 2 или 3?

  4. Что придется изменить, если появится четвертый вид платежа?

  5. Что придется изменить, если появится десять видов платежей?

  6. Какие части программы начнут разрастаться по мере появления новых типов операций?

Сделайте вывод о том, вокруг чего организована процедурная программа:

вокруг последовательностей операций или вокруг объектов предметной области?


объект в Java

До перехода к полной ООП-модели рассмотрите простейший класс:

 
 
public class Payment {

    private double amount;

    public Payment(double amount) {
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }
}

 

В main() создайте объект:

 
 
Payment payment = new Payment(10000);
 

и получите его сумму:

 
 

System.out.println(payment.getAmount());
 

Ответьте

  1. Что здесь является классом?

  2. Что является объектом?

  3. Что делает оператор new?

  4. Что представляет собой поле amount?

  5. Почему поле и значение переменной — не одно и то же понятие?

  6. Что делает конструктор?

  7. Для чего нужен метод getAmount()?



Переход от процедурного решения к ООП

Теперь необходимо рассмотреть тот же пример с платежами в объектно-ориентированной форме.

Использовать следующую структуру:

 
 
Payment
 ├── CardPayment
 ├── SbpPayment
 └── CashPayment
 

Базовый класс:

 
 
public abstract class Payment {

    private final double amount;

    public Payment(double amount) {
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }

    public abstract double calculateCommission();
}

 

Класс оплаты банковской картой:

 
 
public class CardPayment extends Payment {

    public CardPayment(double amount) {
        super(amount);
    }

    @Override
    public double calculateCommission() {
        return getAmount() * 0.02;
    }
}
 

Класс оплаты через СБП студент реализует самостоятельно:

 
 
public class SbpPayment extends Payment {

    // TODO
}
 

Класс оплаты наличными студент также реализует самостоятельно:

 
 
public class CashPayment extends Payment {

    // TODO
}
 


Полиморфная обработка платежей

Создайте массив:

 
 
Payment[] payments = {
        new CardPayment(10000),
        new SbpPayment(2500),
        new CashPayment(5000)
};
 

Обработайте все объекты одним циклом:

 
 
for (Payment payment : payments) {
    System.out.println(
            payment.calculateCommission()
    );
}
 

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


Четыре принципа ООП

Используя разработанную программу, заполните таблицу.

ПринципГде он проявляется в программеЧто дает в данном примере
Абстракция  
Инкапсуляция  
Наследование  
Полиморфизм  

Абстракция

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

Ответьте:

Какие характеристики всех платежей были оставлены существенными, а какие детали конкретных способов оплаты скрыты?

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

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

 
 

private final double amount;
 

Ответьте:

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

  • каким способом другой код получает сумму платежа;

  • какое преимущество это дает.

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

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

 
 
extends Payment
 

Ответьте:

  • какие классы являются потомками;

  • что они получают от базового класса;

  • что каждый из них добавляет или изменяет.

Полиморфизм

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

 
 
Payment payment

 

и вызов:

 
 
payment.calculateCommission();
 

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


 Эксперимент на расширяемость

Добавьте новый вид платежа:

 
 
CryptoPayment
 

Для учебного примера комиссия составляет:

 
 
1,5 %
 

Добавьте объект нового класса в массив Payment[].

Ограничение

После создания CryptoPayment запрещается изменять цикл:

 
 

for (Payment payment : payments) {
    System.out.println(payment.calculateCommission());
}
 

Ответьте

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

Какой принцип ООП это демонстрирует?

Сравните количество изменений:

Процедурный вариант

 
 
добавили новый тип платежа
        ↓
изменяем условие/switch
 

Объектно-ориентированный вариант

 
 
добавили новый тип платежа
        ↓
добавляем новый класс
 

Роль IDE и средств сборки

Кратко ответьте:

  1. Для чего программисту IDE, если Java-программу можно написать в текстовом редакторе и скомпилировать командой javac?

  2. Какие операции IDE выполняет автоматически?

  3. Для чего в крупных Java-проектах применяются Maven или Gradle?

  4. Чем IDE отличается от JDK?

  5. Можно ли написать Java-программу без IntelliJ IDEA?

  6. Можно ли полноценно разрабатывать Java-программы без JDK?



Контрольные вопросы к защите

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

  1. Что такое Java?

  2. Чем Java как язык отличается от Java как платформы?

  3. Что такое JDK?

  4. Что такое JRE?

  5. Что такое JVM?

  6. Как связаны JDK, JRE и JVM?

  7. Для чего используется javac?

  8. Для чего используется java?

  9. Что находится в .java-файле?

  10. Что находится в .class-файле?

  11. Что такое Java bytecode?

  12. Как выполняется Java-приложение?

  13. Для чего нужна JVM?

  14. Что означает Write Once, Run Anywhere?

  15. За счет чего реализован принцип WORA?

  16. В чем отличие процедурного программирования от ООП?

  17. Что такое класс?

  18. Что такое объект?

  19. Что означает абстракция?

  20. Что означает инкапсуляция?

  21. Что означает наследование?

  22. Что означает полиморфизм?

  23. Где каждый из четырех принципов ООП проявляется в выполненном задании?

  24. Для чего используется new?

  25. Какова точка входа Java-программы?

  26. Для чего нужен метод main?

  27. Чем метод Java похож на функцию в C, Go или Python?

  28. Какие базовые типы Java были использованы в лабораторной работе?

  29. Какие управляющие конструкции Java были использованы?

  30. Что пришлось изменить при добавлении нового типа платежа в процедурном варианте и в объектно-ориентированном варианте?

Эти вопросы непосредственно закрывают перечень вопросов первой темы РПД.



Студенты прошли на данный момент маршрут:

 
 
установка Java
        ↓
JDK и IDE
        ↓
.java
        ↓
javac
        ↓
.class / bytecode
        ↓
JVM
        ↓
запуск программы
        ↓
типы и переменные
        ↓
if / switch
        ↓
циклы
        ↓
массивы
        ↓
методы
        ↓
процедурное решение
        ↓
класс и объект
        ↓
объектное решение
        ↓
абстракция
инкапсуляция
наследование
полиморфизм
        ↓
WORA
 
 
 



Лабораторная работа № 1 Часть 3



Подробно как выполняется Java-программа

Общий процесс выглядит так:


Исходный код
HelloJava.java
       │
       │ javac
       ▼
Байт-код
HelloJava.class
       │
       │ java
       ▼
Запуск JVM
       │
       ├── загрузка класса
       ├── проверка байт-кода
       ├── выполнение инструкций
       └── при необходимости JIT-компиляция
       ▼
Машинный код конкретной платформы
 

Команда javac читает исходные файлы .java и создаёт файлы классов .class, предназначенные для выполнения виртуальной машиной. Команда java запускает JVM, загружает указанный класс и вызывает его метод main. Команда javap -c позволяет увидеть инструкции байт-кода.


Что такое байт-код

Байт-код — набор инструкций виртуальной машины Java. Он хранится в файлах с расширением .class.

Байт-код:

  • не является исходным кодом;

  • не является обычным нативным исполняемым файлом Windows, Linux или macOS;

  • описывается спецификацией JVM;

  • выполняется JVM соответствующей платформы.


Write Once, Run Anywhere

Принцип Write Once, Run Anywhere означает:

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

Платформенная независимость достигается за счёт:

  • единого формата файлов .class;

  • единого набора инструкций JVM;

  • реализаций JVM для разных операционных систем и архитектур;

  • стандартной библиотеки Java.

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

  • использует нативные библиотеки;

  • жёстко зависит от путей конкретной ОС;

  • использует системные команды;

  • требует отсутствующие внешние файлы;

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


Процедурный и объектно-ориентированный подходы

КритерийПроцедурный подходОбъектно-ориентированный подход
Основная единица программыФункция или процедураКласс и объект
Данные и операцииОбычно разделеныОбъединены внутри объекта
Организация программыПоследовательность действийВзаимодействие объектов
Изменение поведенияЧасто требует изменения общей функции с условиямиМожно добавить новый класс
Защита состоянияЗависит от дисциплины программистаПоддерживается модификаторами доступа
Подходящие задачиНебольшие алгоритмы, вычисления, утилитыСистемы с большим числом сущностей и изменяемых правил

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


Основные принципы ООП

Абстракция

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

В работе абстракцией будет класс Delivery, который описывает общие свойства любой доставки, но не определяет единую формулу расчёта стоимости.

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

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

Инкапсуляция — это не просто наличие геттеров и сеттеров. Главная задача инкапсуляции — не позволить объекту перейти в некорректное состояние.

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

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

Полиморфизм

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

Объекты объединяют связанное состояние и поведение; Java позволяет создавать иерархии классов, переопределять методы и выбирать нужную реализацию метода во время выполнения.


Быстрый переход от Go, C и Python к Java

ОсобенностьJava
Завершение инструкцииОбычно требуется ;
ТипизацияСтатическая
Точка входаpublic static void main(String[] args)
ФункцииВ традиционной структуре программы объявляются внутри класса
СтрокиОбъекты класса String, являются неизменяемыми
Сравнение строкЧерез .equals(), а не через ==
МассивИмеет фиксированную длину
Создание объектаОператор new
Освобождение памятиАвтоматическое, с помощью сборщика мусора
Арифметика указателейОтсутствует
Имя файлаИмя публичного класса должно совпадать с именем файла
ПакетыИспользуются для организации классов

В этой работе пока не рекомендуется использовать preview-возможности Java, var, Stream API, лямбда-выражения и record. Необходимо сначала освоить обычные типы, циклы, методы и классы - то есть те элементы которые известны из других языков.


Структура проекта

Создайте каталог проекта:

 
 
lab01-java-oop/
├── src/
│   └── edu/
│       └── course/
│           └── lab01/
│               ├── HelloJava.java
│               ├── JavaWarmup.java
│               ├── procedural/
│               │   └── DeliveryProcedural.java
│               └── oop/
│                   ├── Delivery.java
│                   ├── StandardDelivery.java
│                   ├── ExpressDelivery.java
│                   ├── DroneDelivery.java
│                   └── DeliveryApp.java
├── out/
└── README.md
 

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

 
 
C:\java-course\lab01
 

или:

 
 
~/java-course/lab01
 

Ход выполнения работы

Этап 1. Проверка JDK (установка, если не установили еще)

Проверьте установлен ли JDK 25.

При установке в Windows следует включить добавление Java в PATH и настройку JAVA_HOME, если такие варианты предлагает установщик.

Откройте новый терминал и выполните:

 
 
java -version
javac -version
javap -version

 

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

Дополнительно проверьте переменную JAVA_HOME.

Windows:

 
 
echo %JAVA_HOME%
 

Linux и macOS:

 
 
echo $JAVA_HOME

 

В отчёт включить

  • операционную систему;

  • архитектуру процессора;

  • версию JDK;

  • вывод команд java -version и javac -version.

Контрольная ситуация

Если команда java работает, а javac не найдена, наиболее вероятны следующие причины:

  • установлен только runtime-компонент;

  • путь к JDK не добавлен в PATH;

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

  • JAVA_HOME указывает не на тот каталог.


Этап 2. Создание  Java-программы

Создайте файл:

 
 
src/edu/course/lab01/HelloJava.java
 

Содержимое:

 
 
package edu.course.lab01;

public class HelloJava {

    public static void main(String[] args) {
        String name = args.length == 0 ? "студент" : args[0];

        System.out.printf(
                "Привет, %s! Java: %s, JVM: %s, ОС: %s%n",
                name,
                Runtime.version(),
                System.getProperty("java.vm.name"),
                System.getProperty("os.name")
        );
    }
}
 

Из корня проекта выполните:

 
 
mkdir out
javac -d out src/edu/course/lab01/HelloJava.java
java -cp out edu.course.lab01.HelloJava
 

Запустите программу с аргументом:

 
 
java -cp out edu.course.lab01.HelloJava Student
 

Объясните сигнатуру метода

 
 
public static void main(String[] args)
 
  • public — метод доступен средству запуска;

  • static — для вызова метода не нужно создавать объект;

  • void — метод не возвращает значение;

  • main — стандартное имя точки входа;

  • String[] args — массив аргументов командной строки.

В отчёт включить

  • исходный код;

  • команду компиляции;

  • команду запуска;

  • полученный вывод;

  • объяснение назначения main.


Этап 3. Исследование байт-кода

После компиляции найдите файл:

 
 
out/edu/course/lab01/HelloJava.class
 

Выполните:

 
 
javap -classpath out -c -p edu.course.lab01.HelloJava
 

Затем:

 
 
javap -classpath out -verbose edu.course.lab01.HelloJava
 

Требуется

  1. Найти в выводе метод main.

  2. Найти инструкции вызова методов.

  3. Найти строки, использованные программой.

  4. Объяснить, почему файл .class нельзя нормально читать как обычный текст.

  5. Записать не менее трёх названий инструкций байт-кода, например:

    • aload;

    • invokevirtual;

    • invokestatic;

    • return;

    • getstatic.

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


Этап 4. Освоение-повторение базового синтаксиса Java

Создайте файл:

 
 
src/edu/course/lab01/JavaWarmup.java
 
 
 
package edu.course.lab01;

public class JavaWarmup {

    public static void main(String[] args) {
        int[] distances = {3, 7, 2, 10, 5};

        int total = sum(distances);
        int maximum = max(distances);
        double average = (double) total / distances.length;
        int longTrips = countGreaterThan(distances, 5);

        System.out.printf("Сумма: %d%n", total);
        System.out.printf("Среднее: %.2f%n", average);
        System.out.printf("Максимум: %d%n", maximum);
        System.out.printf("Расстояний больше 5 км: %d%n", longTrips);
    }

    static int sum(int[] values) {
        int result = 0;

        for (int value : values) {
            result += value;
        }

        return result;
    }

    static int max(int[] values) {
        // TODO: реализовать без Stream API
        return 0;
    }

    static int countGreaterThan(int[] values, int limit) {
        // TODO: реализовать
        return 0;
    }
}
 

Требуется

  1. Реализовать метод max.

  2. Реализовать метод countGreaterThan.

  3. Добавить метод:

 
 
static int min(int[] values)
 
  1. Добавить вывод минимального значения.

  2. Объяснить, зачем перед делением используется приведение:

 
 
(double) total
 
  1. Добавить условие:

    • расстояние до 3 км — «короткое»;

    • от 4 до 7 км — «среднее»;

    • больше 7 км — «длинное».

Для каждого элемента массива вывести его категорию.

Ожидаемые результаты для исходного массива

 
 
Сумма: 27
Среднее: 5.40
Максимум: 10
Минимум: 2
Расстояний больше 5 км: 2
 

Проверяемые конструкции Java

  • int;

  • double;

  • массив;

  • обычный цикл;

  • расширенный цикл for;

  • if / else;

  • статические методы;

  • параметры;

  • возвращаемые значения;

  • приведение типов;

  • форматированный вывод.


Процедурная реализация задачи

Постановка задачи

Необходимо рассчитать стоимость доставки нескольких заказов.

Используются следующие тарифы:

Вид доставкиФормула
Стандартная120 + 20 × расстояние
Экспресс250 + 35 × расстояние
Дроном300 + 50 × расстояние

В данной учебной задаче стоимость измеряется в условных единицах.

Создайте файл:

 
 
src/edu/course/lab01/procedural/DeliveryProcedural.java
 
 
 
package edu.course.lab01.procedural;

public class DeliveryProcedural {

    public static void main(String[] args) {
        String[] orderNumbers = {"A-101", "B-205", "C-330"};
        String[] types = {"standard", "express", "drone"};
        double[] distances = {3.5, 7.0, 2.0};

        double totalCost = 0.0;

        for (int i = 0; i < orderNumbers.length; i++) {
            double cost = calculateCost(types[i], distances[i]);
            totalCost += cost;

            System.out.printf(
                    "%s | %s | %.1f км | %.2f у.е.%n",
                    orderNumbers[i],
                    types[i],
                    distances[i],
                    cost
            );
        }

        System.out.printf("Итого: %.2f у.е.%n", totalCost);
    }

    static double calculateCost(String type, double distanceKm) {
        if (distanceKm < 0) {
            throw new IllegalArgumentException(
                    "Расстояние не может быть отрицательным"
            );
        }

        if ("standard".equals(type)) {
            // TODO
            return 0.0;
        }

        if ("express".equals(type)) {
            // TODO
            return 0.0;
        }

        if ("drone".equals(type)) {
            // TODO
            return 0.0;
        }

        throw new IllegalArgumentException(
                "Неизвестный вид доставки: " + type
        );
    }
}
 

Требуется

  1. Реализовать три формулы.

  2. Добавить ещё два заказа.

  3. Рассчитать общую стоимость.

  4. Рассчитать количество заказов каждого типа.

  5. Попробовать использовать неизвестный тип доставки.

  6. Попробовать передать отрицательное расстояние.

  7. Заменить одно сравнение строк на:

 
 
type == "standard"
 

и объяснить, почему такое сравнение считается ошибочным подходом.

Анализ процедурной версии

Ответьте письменно:

  1. Что произойдёт, если длины массивов окажутся разными?

  2. Где находятся данные заказа?

  3. Где находится поведение заказа?

  4. Сколько мест программы потребуется изменить для добавления нового тарифа?

  5. Почему функция calculateCost будет увеличиваться по мере появления новых типов?

  6. Можно ли создать заказ без номера или с отрицательным расстоянием?

  7. Кто отвечает за согласованность элементов параллельных массивов?


 Далее активно используется ИИ, так как компетенции необходимые для реализации программы еще не получены, но необходимо сравнить процедурные и ООП подходы на этом этапе

Объектно-ориентированная реализация

Теперь необходимо переработать ту же программу с использованием ИИ в объектно-ориентированном стиле.

1. Абстрактный класс Delivery

Создайте файл:

 
 
src/edu/course/lab01/oop/Delivery.java

 
 
 
package edu.course.lab01.oop;

public abstract class Delivery {

    private final String orderNumber;
    private final double distanceKm;

    protected Delivery(String orderNumber, double distanceKm) {
        if (orderNumber == null || orderNumber.isBlank()) {
            throw new IllegalArgumentException(
                    "Номер заказа не должен быть пустым"
            );
        }

        if (distanceKm < 0) {
            throw new IllegalArgumentException(
                    "Расстояние не может быть отрицательным"
            );
        }

        this.orderNumber = orderNumber;
        this.distanceKm = distanceKm;
    }

    public final String getOrderNumber() {
        return orderNumber;
    }

    public final double getDistanceKm() {
        return distanceKm;
    }

    public abstract double calculateCost();

    protected abstract String typeName();

    public final String summary() {
        return "%s | %s | %.1f км | %.2f у.е."
                .formatted(
                        orderNumber,
                        typeName(),
                        distanceKm,
                        calculateCost()
                );
    }
}
 

Обратите внимание

Класс объявлен как:

 
 
public abstract class Delivery
 

Следовательно, нельзя написать:

 
 
new Delivery(...)
 

Абстрактный класс описывает общие характеристики доставки, но не представляет конкретный тариф.


2. Стандартная доставка

Создайте файл:

 
 
src/edu/course/lab01/oop/StandardDelivery.java

 
 
 
package edu.course.lab01.oop;

public final class StandardDelivery extends Delivery {

    private static final double BASE_PRICE = 120.0;
    private static final double PRICE_PER_KM = 20.0;

    public StandardDelivery(String orderNumber, double distanceKm) {
        super(orderNumber, distanceKm);
    }

    @Override
    public double calculateCost() {
        return BASE_PRICE + PRICE_PER_KM * getDistanceKm();
    }

    @Override
    protected String typeName() {
        return "Стандартная";
    }
}

 

3. Экспресс-доставка

На этом этапе используется ИИ, так как соотвествующие темы не освоены. Задание

Создайте класс с использованием ИИ :

 
 
public final class ExpressDelivery extends Delivery
 

Требования:

  • базовая стоимость — 250;

  • стоимость километра — 35;

  • реализовать конструктор;

  • переопределить calculateCost;

  • переопределить typeName;

  • использовать аннотацию @Override;

  • значения тарифов оформить как private static final.


4. Доставка дроном

Создайте класс:

 
 
public final class DroneDelivery extends Delivery
 

Требования:

  • базовая стоимость — 300;

  • стоимость километра — 50;

  • максимальное расстояние — 10 км;

  • если расстояние больше 10 км, конструктор должен выбрасывать IllegalArgumentException;

  • реализовать calculateCost;

  • реализовать typeName;

  • значения тарифов и ограничения оформить как константы.


5. Главный класс приложения

Создайте файл:

 
 
src/edu/course/lab01/oop/DeliveryApp.java

 

 
 
package edu.course.lab01.oop;

public class DeliveryApp {

    public static void main(String[] args) {
        Delivery[] deliveries = {
                new StandardDelivery("A-101", 3.5),
                new ExpressDelivery("B-205", 7.0),
                new DroneDelivery("C-330", 2.0)
        };

        double totalCost = 0.0;

        for (Delivery delivery : deliveries) {
            System.out.println(delivery.summary());
            totalCost += delivery.calculateCost();
        }

        System.out.printf("Итого: %.2f у.е.%n", totalCost);
    }
}
 

Ожидаемый результат

 
 
A-101 | Стандартная | 3.5 км | 190.00 у.е.
B-205 | Экспресс | 7.0 км | 495.00 у.е.
C-330 | Дрон | 2.0 км | 400.00 у.е.
Итого: 1085.00 у.е.

 

Разделитель дробной части может зависеть от региональных настроек операционной системы.


Обязательные ограничения объектной версии

В классе DeliveryApp запрещено:

  • проверять тип доставки через if;

  • проверять тип доставки через switch;

  • использовать instanceof для выбора формулы;

  • хранить текстовый код типа доставки;

  • повторять формулы тарифов;

  • обращаться непосредственно к полям класса Delivery.

Поля объектов должны быть private.

Расчёт стоимости должен выполняться только через вызов:

 
 
delivery.calculateCost()
 

Именно это требование демонстрирует полиморфизм.


Как четыре принципа ООП представлены в программе

Абстракция

 
 
public abstract class Delivery
 

и:

 
 
public abstract double calculateCost();
 

Класс задаёт общий контракт, но не раскрывает единую реализацию расчёта.

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

 
 
private final String orderNumber;
private final double distanceKm;
 

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

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

 
 
public final class StandardDelivery extends Delivery
 

Конкретный тип доставки наследует общие свойства и методы класса Delivery.

Полиморфизм

 
 
Delivery[] deliveries = {
        new StandardDelivery(...),
        new ExpressDelivery(...),
        new DroneDelivery(...)
};
 

Переменная имеет тип Delivery, однако JVM вызывает метод того фактического класса, объект которого находится в массиве.




Компиляция объектной версии из терминала

Из корня проекта выполните одной строкой:

 
 
javac -d out src/edu/course/lab01/oop/Delivery.java src/edu/course/lab01/oop/StandardDelivery.java src/edu/course/lab01/oop/ExpressDelivery.java src/edu/course/lab01/oop/DroneDelivery.java src/edu/course/lab01/oop/DeliveryApp.java
 

Запуск:

 
 
java -cp out edu.course.lab01.oop.DeliveryApp
 

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


Исследование полиморфизма в байт-коде

Выполните:

 
 
javap -classpath out -c -p edu.course.lab01.oop.DeliveryApp
 

и:

 
 
javap -classpath out -c -p edu.course.lab01.oop.StandardDelivery
 

Требуется найти

  • инструкцию создания объекта;

  • вызов конструктора;

  • вызов calculateCost;

  • вызов summary;

  • инструкцию возврата значения типа double.

В отчёте объясните, почему в байт-коде DeliveryApp нет отдельных формул для стандартной, экспресс- и дрон-доставки.


Создание исполняемого JAR-файла

Выполните:

 
 
jar --create --file lab01.jar --main-class edu.course.lab01.oop.DeliveryApp -C out .

 

Запустите:

 
 
java -jar lab01.jar
 

Проверьте, что результат совпадает с запуском через java -cp.


Контрольные вопросы для защиты

  1. В чём ключевое различие процедурной и объектно-ориентированной декомпозиции программы?

  2. Что такое класс?

  3. Что такое объект?

  4. Чем состояние объекта отличается от его поведения?

  5. Что означает абстракция?

  6. Чем абстракция отличается от инкапсуляции?

  7. Почему поля Delivery объявлены как private?

  8. Как конструктор помогает поддерживать корректное состояние объекта?

  9. Что наследуют StandardDelivery, ExpressDelivery и DroneDelivery?

  10. Почему нельзя создать объект абстрактного класса Delivery?

  11. Что означает аннотация @Override?

  12. Какой метод будет вызван для объекта DroneDelivery, если переменная имеет тип Delivery?

  13. Почему в объектной версии не нужен switch по виду доставки?

  14. Что такое JVM?

  15. Что входит в JDK?

  16. Что принято называть JRE?

  17. Как связаны JDK, JRE и JVM?

  18. Что создаёт команда javac?

  19. Что делает команда java?

  20. Что показывает команда javap -c?

  21. Что такое байт-код?

  22. Почему .class не является обычным исполняемым файлом Windows или Linux?

  23. За счёт чего реализуется Write Once, Run Anywhere?

  24. Почему программа, скомпилированная новой версией Java, может не запуститься на старой JVM?

  25. Какие зависимости от операционной системы могут нарушить переносимость?

  26. Почему строки в Java следует сравнивать через .equals(), а не через ==?

  27. Что произойдёт при целочисленном делении int на int?

  28. Почему методы JavaWarmup объявлены как static, а методы доставок — как обычные методы объектов?

  29. Какие изменения потребуются для добавления нового тарифа в процедурную версию?

  30. Какие изменения потребуются для добавления нового тарифа в объектную версию?


Особое внимание следует обратить на различие:

  • абстракция отвечает на вопрос «что объект должен уметь?»;

  • инкапсуляция отвечает на вопрос «как объект защищает своё состояние и скрывает детали?»;

  • наследование отвечает на вопрос «как новый класс связан с общим типом?»;

  • полиморфизм отвечает на вопрос «как один вызов приводит к разному поведению объектов?».

 


Рейтинг@Mail.ru