Нужно уметь ответить на вопросы по теории ООП, а также уметь настраивать минимально необходимое окружение.
Введение в Java и объектно-ориентированное программирование
Предварительные знания
Студент должен понимать:
переменные и типы данных;
условия;
циклы;
функции;
массивы;
базовые принципы алгоритмизации.
Знание Java не требуется. Предполагается, что студенты ранее работали с Go и знакомы с C и Python.
Цель работы
Познакомиться с платформой Java и средствами разработки, научиться создавать, компилировать и запускать простые Java-программы, освоить базовый синтаксис Java на основе уже имеющегося опыта программирования на (Go, C, если есть такой опыт) и Python, а также практически увидеть различия между процедурным и объектно-ориентированным подходами.
После выполнения работы студент должен понимать:
назначение JVM, JDK и JRE;
что представляет собой Java bytecode;
этапы компиляции и выполнения Java-программы;
смысл принципа Write Once, Run Anywhere;
структуру простой программы на Java;
основные типы данных, переменные, операторы, условия, циклы, массивы и методы;
различия процедурного и объектно-ориентированного подходов;
смысл принципов ООП: абстракции, инкапсуляции, наследования и полиморфизма.
1. Подготовка среды разработки
Для выполнения работы необходимо установить:
JDK — Java Development Kit.
IDE для Java, рекомендуется Visual Studio Code или IntelliJ IDEA Community Edition .
Терминал операционной системы.
Версию JDK могут отличаться, лучше поставить свежую.
После установки открыть терминал и выполнить:
java -versionjavac -version
В отчёт записать полученные версии.
Контрольный вопрос
Объясните, почему после установки JDK доступны две разные команды:
java javac
Необходимо выяснить назначение каждой из них.
Краткие теоретические сведения
JDK, JRE и JVM
Упрощённая учебная схема выглядит следующим образом:
JDK — Java Development Kit
│
├── инструменты разработки
│ ├── javac — компилятор
│ ├── javap — просмотр структуры и байт-кода классов
│ ├── jar — создание JAR-архивов
│ └── другие инструменты
│
└── среда выполнения
├── JVM
└── стандартные библиотеки JavaJVM — 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
Ответьте письменно:
Какой файл содержит исходный код?
Какой файл появился после компиляции?
Почему для запуска используется:
java Main
а не
java Main.class
Является ли файл Main.class машинным кодом конкретного процессора?
Какую роль при запуске выполняет 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. Сравнение со знакомыми языками
Заполните небольшую таблицу самостоятельно.
| Конструкция | Python | Java |
|---|---|---|
| точка входа | ? | 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 — операция через СБП
Задание
Ответьте:
Где находятся данные?
Где находится логика работы с данными?
Что произойдёт с методом calculateCommission(), если появятся ещё 10 видов операций?
Почему использование чисел 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 %
Требования:
Создать новый класс.
Наследовать его от Transaction.
Реализовать calculateCommission().
Добавить объект нового класса в массив:
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. Контрольные вопросы
При защите студент должен ответить на вопросы.
Чем процедурный подход отличается от объектно-ориентированного?
Что такое объект?
Что такое класс?
Что означает абстракция?
Что означает инкапсуляция?
Что означает наследование?
Что означает полиморфизм?
Что представляет собой JVM?
Для чего нужен JDK?
Что понимается под JRE?
Чем javac отличается от java?
Что представляет собой Java bytecode?
Почему Java обычно не компилируется непосредственно в инструкции конкретного процессора?
Как выполняется программа после запуска команды java Main?
Что означает принцип Write Once, Run Anywhere?
Почему наличие JVM позволяет запускать один байт-код на разных операционных системах?
Что находится в файле .java?
Что находится в файле .class?
Для чего нужен метод:
public static void main(String[] args)
Какие базовые типы Java использовались в работе?
Чем метод в Java похож на функцию в C, Go или Python?
Что делает ключевое слово new?
Для чего используется private?
Что делает extends?
Что означает аннотация @Override в рассмотренном примере?
17. Что должно быть представлено преподавателю
Студент предоставляет:
Проект JavaLab01.
Исходный код выполненных заданий.
Скриншот проверки:
java -version javac -version
Скриншот программы, запущенной из IntelliJ IDEA или VS Code
Результат выполнения процедурной версии.
Результат выполнения объектно-ориентированной версии.
Реализованный CashTransaction.
Краткий отчёт.
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?
Что придется изменить, если появится четвертый вид платежа?
Что придется изменить, если появится десять видов платежей?
Какие части программы начнут разрастаться по мере появления новых типов операций?
Сделайте вывод о том, вокруг чего организована процедурная программа:
вокруг последовательностей операций или вокруг объектов предметной области?
объект в 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());
Ответьте
Что здесь является классом?
Что является объектом?
Что делает оператор new?
Что представляет собой поле amount?
Почему поле и значение переменной — не одно и то же понятие?
Что делает конструктор?
Для чего нужен метод 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 и средств сборки
Кратко ответьте:
Для чего программисту IDE, если Java-программу можно написать в текстовом редакторе и скомпилировать командой javac?
Какие операции IDE выполняет автоматически?
Для чего в крупных Java-проектах применяются Maven или Gradle?
Чем IDE отличается от JDK?
Можно ли написать Java-программу без IntelliJ IDEA?
Можно ли полноценно разрабатывать Java-программы без JDK?
Контрольные вопросы к защите
Студент должен уметь без использования конспекта ответить на следующие вопросы:
Что такое Java?
Чем Java как язык отличается от Java как платформы?
Что такое JDK?
Что такое JRE?
Что такое JVM?
Как связаны JDK, JRE и JVM?
Для чего используется javac?
Для чего используется java?
Что находится в .java-файле?
Что находится в .class-файле?
Что такое Java bytecode?
Как выполняется Java-приложение?
Для чего нужна JVM?
Что означает Write Once, Run Anywhere?
За счет чего реализован принцип WORA?
В чем отличие процедурного программирования от ООП?
Что такое класс?
Что такое объект?
Что означает абстракция?
Что означает инкапсуляция?
Что означает наследование?
Что означает полиморфизм?
Где каждый из четырех принципов ООП проявляется в выполненном задании?
Для чего используется new?
Какова точка входа Java-программы?
Для чего нужен метод main?
Чем метод Java похож на функцию в C, Go или Python?
Какие базовые типы Java были использованы в лабораторной работе?
Какие управляющие конструкции Java были использованы?
Что пришлось изменить при добавлении нового типа платежа в процедурном варианте и в объектно-ориентированном варианте?
Эти вопросы непосредственно закрывают перечень вопросов первой темы РПД.
Студенты прошли на данный момент маршрут:
установка 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
Требуется
Найти в выводе метод main.
Найти инструкции вызова методов.
Найти строки, использованные программой.
Объяснить, почему файл .class нельзя нормально читать как обычный текст.
Записать не менее трёх названий инструкций байт-кода, например:
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;
}
}Требуется
Реализовать метод max.
Реализовать метод countGreaterThan.
Добавить метод:
static int min(int[] values)
Добавить вывод минимального значения.
Объяснить, зачем перед делением используется приведение:
(double) total
Добавить условие:
расстояние до 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
);
}
}Требуется
Реализовать три формулы.
Добавить ещё два заказа.
Рассчитать общую стоимость.
Рассчитать количество заказов каждого типа.
Попробовать использовать неизвестный тип доставки.
Попробовать передать отрицательное расстояние.
Заменить одно сравнение строк на:
type == "standard"
и объяснить, почему такое сравнение считается ошибочным подходом.
Анализ процедурной версии
Ответьте письменно:
Что произойдёт, если длины массивов окажутся разными?
Где находятся данные заказа?
Где находится поведение заказа?
Сколько мест программы потребуется изменить для добавления нового тарифа?
Почему функция calculateCost будет увеличиваться по мере появления новых типов?
Можно ли создать заказ без номера или с отрицательным расстоянием?
Кто отвечает за согласованность элементов параллельных массивов?
Далее активно используется ИИ, так как компетенции необходимые для реализации программы еще не получены, но необходимо сравнить процедурные и ООП подходы на этом этапе
Объектно-ориентированная реализация
Теперь необходимо переработать ту же программу с использованием ИИ в объектно-ориентированном стиле.
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.
Контрольные вопросы для защиты
В чём ключевое различие процедурной и объектно-ориентированной декомпозиции программы?
Что такое класс?
Что такое объект?
Чем состояние объекта отличается от его поведения?
Что означает абстракция?
Чем абстракция отличается от инкапсуляции?
Почему поля Delivery объявлены как private?
Как конструктор помогает поддерживать корректное состояние объекта?
Что наследуют StandardDelivery, ExpressDelivery и DroneDelivery?
Почему нельзя создать объект абстрактного класса Delivery?
Что означает аннотация @Override?
Какой метод будет вызван для объекта DroneDelivery, если переменная имеет тип Delivery?
Почему в объектной версии не нужен switch по виду доставки?
Что такое JVM?
Что входит в JDK?
Что принято называть JRE?
Как связаны JDK, JRE и JVM?
Что создаёт команда javac?
Что делает команда java?
Что показывает команда javap -c?
Что такое байт-код?
Почему .class не является обычным исполняемым файлом Windows или Linux?
За счёт чего реализуется Write Once, Run Anywhere?
Почему программа, скомпилированная новой версией Java, может не запуститься на старой JVM?
Какие зависимости от операционной системы могут нарушить переносимость?
Почему строки в Java следует сравнивать через .equals(), а не через ==?
Что произойдёт при целочисленном делении int на int?
Почему методы JavaWarmup объявлены как static, а методы доставок — как обычные методы объектов?
Какие изменения потребуются для добавления нового тарифа в процедурную версию?
Какие изменения потребуются для добавления нового тарифа в объектную версию?
Особое внимание следует обратить на различие:
абстракция отвечает на вопрос «что объект должен уметь?»;
инкапсуляция отвечает на вопрос «как объект защищает своё состояние и скрывает детали?»;
наследование отвечает на вопрос «как новый класс связан с общим типом?»;
полиморфизм отвечает на вопрос «как один вызов приводит к разному поведению объектов?».
