Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
В данной лабораторной работе мы установим необходимое программное обеспечение и познакомимся с базовым синтаксисом и операторами языка Java.
Для работы с Java, вам нужно установить JDK и среду разработки.
Java Development Kit (сокращенно JDK) — бесплатно распространяемый компанией Oracle Corporation (ранее Sun Microsystems) комплект разработчика приложений на языке Java, включающий в себя компилятор Java (javac), стандартные библиотеки классов Java, примеры, документацию, различные утилиты и исполнительную систему Java (JRE).
Для установки JDK необходимо скачать установщик по следующей ссылке (также вы можете загуглить "download jdk" и перейти по первой ссылке).
Установка JDK, как правило, проходит без проблем, настраивать параметры установки не требуется.
Интегрированная среда разработки (Integrated development environment — IDE) — комплекс программных средств, используемый программистами для разработки программного обеспечения.
Для Java существует "большая тройка" сред разработки:
Все остальные среды являются более простыми и менее функциональными.
В рамках данного курса будет использоваться среда IntelliJ IDEA, которая считается самой популярной IDE для Java. Вы можете выполнять лабораторные работы любым удобным вам способом, в любой удобной вам среде разработки.
В рамках данного курса будет использована Community версия среды IntelliJ IDEA. Скачиваем и запускаем с сайта разработчика установщик.
В процессе установки желательно выбрать следующие параметры установки
Кроме установки среды разработки, нам необходимо добавить дополнительные плагины - EduTools и CheckStyle.
Запустим IntelliJ IDEA, после чего выберем пункт Configure -> Plugins.
Найдите в поисковой строке EduTools и установим его.
После этого введем в поисковой строке CheckStyle и также установим его. После этого необходимо перезапустить среду разработки.
При первом запуске вас попросят выбрать цветовую схему среды разработки. Остальные параметры можете опустить. В стартовом окне выбираем пункт "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
либо нажать на зеленый треугольник справа в верхней части редактора.
Образец протокола лабораторной работы:
в формате pdf
Корректно работающая лабораторная работа оценивается в 60 баллов, кроме оценки D за протокол лабораторной работы или в случае списывания лабораторной работы.
Студент получает дополнительно:
до 20 баллов за стилизацию исходного кода;
до 10 баллов за оформление протокола лабораторной работы;
до 10 баллов за качество кода.
Скачайте архив с заданием на первую лабораторную работу.
Откройте IntelliJ IDEA (предварительно установите плагин EduTools), выберите пункт Learn and Teach -> Import Local Course
Выберите архив с заданием
Откройте задание с помощью кнопки Open
Задание на лабораторную работу состоит из 7 задач, которые необходимо решить
Для решения каждой задачи требуется написать код для метода и вернуть правильное значение. Условие работы метода описано в правой части окна. После написания кода, нажмите кнопку Check, чтобы проверить, правильно ли вы реализовали метод.
Давайте рассмотрим выполнение лабораторной работы на примере первой задачи.
Нам необходимо реализовать метод sleepIn()
. Согласно условию задачи, метод принимает на вход булеву переменную weekday
(будний ли день) и переменную vacation
(отпуск или нет).
Давайте нажмем кнопку Check и посмотрим, что произойдет.
Как мы видим, изначально задача не решена, нам необходимо корректно реализовать метод.
Согласно условию задачи, метод возвращает true
, если выходной или отпуск. Очевидно, что код метода будет выглядеть следующим образом.
Проверим правильность написания кода.
Ура, задача решена. Для успешной сдачи лабораторной работы необходимо выполнить все 7 задач.
На всякий случай, продублируем условия задач на первую лабораторную работу
Метод sleepIn() принимает решение о том, необходимо ли нам проснуться или можно еще поспать. Нужно проснуться, если будний день и нет отпуска
Параметр weekday определяет, будний ли день
Параметр vacation определяет, отпуск или нет
Метод возвращает true, если можно еще поспать и false в обратном случае
Дано два числа, метод closeToTen() возвращает число, которое наиболее близко к 10. Если оба числа равноблизки к 10, то метод возвращает 0.
Подсказка: используйте метод Math.abs() для получения абсолютного значения числа. Пример:
closeToTen(8, 13) -> 8 (число 8 ближе к 10)
closeToTen(8, 11) -> 11 (число 11 ближе к 10)
closeToTen(13, 7) -> 0 (оба числа равноблизки к 10)
Метод принимает на вход массив и возвращает массив в обратном порядке
Пример:
reverseArray([1,2,3]) -> [3,2,1]
reverseArray([7,0,0]) -> [0,0,7]
Метод принимает на вход два массива и возвращает массив, сумма элементов которого больше. Если суммы элементов массивов равны, метод возвращает первый массив.
Пример:
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]
Метод принимает на вход число 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]
Дан непустой массив. Метод возвращает 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])
Дан массив, метод возвращает true если каждый элемент массива равен или больше предыдущему
Пример:
scoresIncreasing([1, 3, 4]) -> true
scoresIncreasing([1, 3, 2]) -> false
scoresIncreasing([1, 1, 4]) -> true
Реализуйте игру "Крестики-нолики". В игру играют два человека, поочередно вводя координаты поля, куда необходимо поставить крестик или нолик. Ввод происходит в формате [строка] [столбец]
. Например: 0 0
или 1 2
.
Сценарий игры будет примерно следующим
Ниже представлен возможный вариант метода для отрисовки поля. В данном случае подразумевается, что в двумерном массиве число 0
соответствует пустому полю, число 1
соответствует X
, а число - 1
соответствует 0
.
Блок-схема алгоритма программы может быть примерно следующей.
В качестве дополнительного задания необходимо предусмотреть метод для проверки корректности ввода: можно ставить крестик или нолик только в свободную ячейку, а также отслеживать выход за пределы поля при указании номера строки и столбца.
Если пользовательский ввод оказался некорректным, то программа просит игрока повторить ход.
При разработке языка Java был взят за основу синтаксис языков С и C++, поэтому, многие аспекты синтаксиса языка покажутся вам знакомыми.
В Java, как и в C, существуют однострочные и блоковые комментарии. Однако, кроме этого, согласно конвенции Oracle, существуют другие виды комментариев: copyright-блок вверху, doc-комментарии, TODO-комментарии, комментарии после statement`ов, комментарии для комментирования кода. Ознакомьтесь с принятыми правилами использования комментариев, на защите лабораторных они будут требоваться в обязательном порядке, согласно принятым конвенциям.
Переменные чувствительны к регистру (variable
и Variable
– две разные переменные), могут быть бесконечной длины, состоять из букв юникода и цифр юникода, символов _
и $
.
Первый символ переменной может быть буквой, символом _
или $
(использовать _
или $
первым символом КРАЙНЕ НЕ РЕКОМЕНДУЕТСЯ, они существуют для специальных ситуаций, которые нас сейчас не интересуют, так что считайте, что начинаться переменная может только с буквы юникода). Крайне не рекомендуется использовать буквы национальных алфавитов, кириллицу, транслит. Только латинские буквы, названия на английском. Также, названия переменных не должны совпадать со списком зарезервированных слов, который представлен ниже.
Кроме ключевых слов в Java существуют три литерала: null
, true
, false
, не относящиеся к ключевым и зарезервированным словам, а также зарезервированное слово var
, значение которого зависит от его позиции в коде.
Приведенный ниже блок кода даст вам общее представление о том, как надо называть переменные
В языке Java существуют примитивные типы (аналогичны типам данных в C) и ссылочные (или объектные) типы данных. На данный момент нас интересуют только примитивные типы данных.
Java – строго типизированный язык программирования. Это значит, что переменная, перед использованием, должна быть объявлена и ей должен быть присвоен тип, который нельзя поменять. Также, при выполнении операций присваивания, компилятор проверяет соответствие типов (какого-то механизма автоматического приведения типов у Java нет).
Всего существуют восемь примитивных типов данных: int, long, short, byte, double, float, char, boolean. Их очень легко запомнить:
4 типа для целых чисел («короткое short», «среднее int», «длинное long» и байт);
2 типа для чисел с плавающей запятой (старая парочка double и float);
2 «специальных» типа – символ и булевый тип.
Операторы ветвления в C и Java практически идентичны
Работа с циклами в Java мало чем отличается от языка C
Работа с массивами в Java несколько отличается от работы с массивами в C, в основном, из-за механизма выделения памяти под массивы.
Так как Java является объектно-ориентированным языком, функции здесь называются методами (на данный момент мы будем считать, что методы и функции выполняют одну и ту же роль, но методы могут находиться только внутри классов).
Оценка
Стилизация исходного кода (до 20 баллов)
Оформление протокола (до 10 баллов)
Качество кода (до 10 баллов)
A
Исходный код без заметных нареканий, названия, отступы, пробелы реализованы грамотно, комментарии по делу.
18-20 баллов
Оформление без видимых замечаний, присутствуют осмысленные пояснения к коду. Код вставлен текстом с соблюдением отступов и моноширинным шрифтом. Присутствуют выводы к лабораторной работе.
8-10 баллов
Циклы, ветвления грамотно оформлены. Инициализация переменных выполнена грамотно. Выдержана правильная структура класса.
8-10 баллов
B
Есть замечания по стилю, отступам или названиям. Комментарии избыточны или недостаточны.
15-17 баллов
Оформление с замечаниями, код вставлен изображением или отсутствуют отступы. Пояснения к коду слишком краткие или формальные. Выводы к лабораторной работе формальные.
6-7 баллов
Неоптимальная форма циклов (отступление от каноничной формы без видимой причины) и ветвлений (пустые блоки).
5-7 баллов
C
Названия, отступы, стилизация соблюдаются спорадически. Комментарии отсутствуют или присутствуют на каждой строчке.
10-14 баллов
Оформление с существенными замечаниями, код вставлен не моноширинным шрифтом, не соблюдены отступы. Пояснения к коду отсутствуют. Выводы к лабораторной работе формальные или отсутствуют.
0-5 баллов
Циклы избыточно сложны, очень сложные условия ветвлений, инициализация переменных выполнена в стиле языка C, неиспользуемые переменные.
2-4 балла
D
Правила грамотного написания кода не соблюдены. Отступы, названия, стилизация не соответствуют современным правилам оформления кода.
0-9 баллов
В протоколе некорректно указан номер группы и(-или) студент. Код нечитабелен, пояснения отсутствуют, выводы отсутствуют. Протокол отсутствует.
Лабораторная не принимается.
Не выдержана структура класса, некорректные ветвления и циклы.
0-1 балл
abstract | continue | for | new | switch |
assert | default | goto | package | synchronized |
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 |
Type | Min | Max | RAM | Default | Объявления и литералы |
byte | -128 | 127 | 8 bit | 0 | byte b = 100; |
short | -32,768 | 32,767 | 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; |
изучить принцип инкапсуляции и его реализацию в языке Java;
ознакомиться с базовой нотацией диаграммы классов UML;
научиться представлять классы в виде нотации диаграммы классов UML;
научиться "читать" диаграмму и писать код исходя из диаграммы классов UML.
изучить принцип наследования;
изучить принцип композиции;
разобраться в чем отличие наследования от композиции и когда следует применять тот или иной принцип;
научиться отображать композицию и наследование в диаграмме классов UML.
Создайте класс с названием TimeSpan
. Объект класса TimeSpan
хранит интервал времени в часах и минутах (к примеру, временной интервал между 8:00 и 10:30 это 2 часа 30 минут). Класс TimeSpan
должен иметь следующие методы
Вы должны продумать:
как хранить значение временного интервала;
типы входных аргументов методов;
корректно реализовать требуемые методы;
в методах предусмотреть проверки на корректность входных данных.
Добавьте к написанному ранее классу метод subtract()
со следующей сигнатурой
Метод вычитает из текущего временного интервала входной временной интервал. Если входной интервал больше текущего, выйдите из метода и не модифицируйте текущий интервал (можете выбросить исключение IllegalArgumentException
, если знаете что это и знаете как выбрить исключение).
Добавьте к написанному ранее классу метод subtract()
со следующей сигнатурой
Метод увеличивает текущий интервал в factor
число раз. Например, если текущий временной интервал равен 1 час 45 минут, а входной аргумент равен 2, то интервал увеличится до 3 часов 30 минут. Убедитесь, что factor
- неотрицательное число, помните про пересчет минут в часы при увеличении интервала.
В программе существует класс BankAccount
, исходный код которого приведен ниже.
Каждый объект класса предназначен для описания одного счета клиента банка, включая его имя и баланс. Модифицируйте класс следующим образом:
добавьте поле transactionFee
типа double
, которое хранит сумму, которая вычитается из баланса каждый раз, когда клиент банка снимает деньги (метод withdraw()
). Изначальное значение равно 0.00
, но значение может быть изменено в дальнейшем. Производите вычитание суммы каждый раз, когда клиент осуществляет снятие денег;
сделайте так, чтобы в результате снятия денег и снятия transactionFee
, баланс счета не мог опуститься ниже нуля. Если в результате снятия денег и transactionFee
баланс может стать отрицательным, выйдите из метода и не производите модификацию баланса вообще;
в методe deposit()
входной аргумент не должен быть ноль или меньше;
в методе withdraw()
входной аргумент не должно быть ноль или меньше.
Модификация класса может потребовать создания новых методов и полей.
Добавьте к модифицированному ранее классу BankAccout
метод transfer()
со следующей сигнатурой
Метод transfer()
осуществляет перевод денег из одного счета (текущего) на другой счет (объект receiver
). Первый параметр - счет получателя денег, второй параметр - количество денег, которые переводятся со счета на счет.
Важно!
При снятии денег должна учитываться комиссия (поле transactionFee
). Следовательно, из текущего объекта должна быть вычтено amount + transactionFee
.
Метод должен модифицировать баланс двух объектов. У текущего объекта должна вычтена сумма amount + transactionFee
из баланса, а у объекта receiver
должен быть пополнен баланс счета на сумму amount
.
При реализации метода убедитесь, что amount > 0
.
Если у текущего объекта баланс меньше amount + transactionFee
, то выйдите из метода без какого-либо перевода денег.
Создайте класс Student
, который хранит информацию о студенте. У студента есть имя, год обучения (от 1 до 4), а также массив изучаемых дисциплин.
Класс Student
должен иметь следующие методы
Вы должны продумать:
как хранить данные студенты (типы данных, структуры и так далее);
типы входных аргументов методов и тип возвращаемого значения;
корректно реализовать требуемые методы;
в методах предусмотреть проверки на корректность входных данных.
Объектно-ориентированное программирование – это методология программирования, основанная на представлении программы в виде совокупности взаимодействующих объектов, каждый из которых является экземпляром определенного класса, а классы являются членами определенной иерархии наследования.
Объект – структура, которая объединяет данные и методы, которые эти данные обрабатывают. Фактически, объект является основным строительным блоком объектно-ориентированных программ.
Об объектах можно думать как о полезных существах, которые "живут" в нашей программе и коллективно решают некоторую задачу. Наша обязанность заключается в том, чтобы создать эти существа, распределить между ними обязанности, регламентировать сценарий их поведения и взаимодействия при решении поставленной задачи.
Класс – шаблон для объектов. Каждый объект является экземпляром (instance) какого-либо класса («безклассовых» объектов не существует). В рамках класса задается общий шаблон, структура, на основании которой создаются объекты. Данные, относящиеся к классу, называются полями класса, а программный код для их обработки называется методами класса. Поля и методы называют общим термином – члены класса.
Разница между классом и объектом такая же, как между абстрактным понятием и реальным объектом.
Объект состоит из следующих частей:
имя объекта;
состояние (переменные состояния). Данные, содержащиеся в объекте, представляют его состояние. В терминологии ООП эти данные называются атрибутами. Например, атрибутами работника могут быть: имя, фамилия, пол, дата рождения, номер телефона. В разных объектах атрибуты имеют разное значение. Фактически, в объектах определяются конкретные значения тех переменных (полей класса), которые были заявлены при описании класса;
методы (операции) – применяются для выполнения операций с данными, а также для совершения других действий. Методы определяют, как объект взаимодействует с окружающим миром.
В теории ООП, объекты могут отправлять друг другу сообщения. В языках программирования этот механизм реализуется через вызов функции другого объекта. Понятие "сообщение" в ООП можно объяснить следующим образом: мы не можем напрямую изменить состояние объекта и должны как бы послать сообщение объекту, что мы хотим как-то изменить его состояние. Очень важно понять, что объект сам меняет свое состояние, а мы можем только попросить его об этом с помощью отсылки сообщения.
В объектно-ориентированной программе весь код должен находиться внутри классов!
В классе описываются, какого типа данные относятся к классу, а также то, какие методы применяются к этим данным. Затем, в программе на основе того или иного класса создается экземпляр класса (объект), в котором указываются конкретные значения полей и выполняются необходимые действия над ними.
В окончательном виде любая программа представляет собой набор инструкций процессора. Все, что написано на любом языке программирования - более удобная, упрощенная запись этого набора инструкций, облегчающая написание, отладку и последующую модификацию программы. Чем выше уровень языка, тем в более простой форме записываются одни и те же действия.
С ростом объема программы становится невозможным удерживать в памяти все детали, и становится необходимым структурировать информацию, выделять главное и отбрасывать несущественное. Этот процесс называется повышением степени абстракции программы.
Для языка высокого уровня первым шагом к повышению абстракции является использование функций, позволяющее после написания и отладки функции отвлечься от деталей ее реализации, поскольку для вызова функции требуется знать только ее интерфейс. Если глобальные переменные не используются, интерфейс полностью определяется заголовком функции.
Следующий шаг - описание собственных типов данных, позволяющих структурировать и группировать информацию, представляя ее в более естественном виде. Например, все разнородные сведения, относящиеся к одному виду товара на складе, можно представить с помощью одной структуры.
Для работы с собственными типами данных требуются специальные функции. Естественно сгруппировать их с описанием этих типов данных в одном месте программы, а также по возможности отделить от ее остальных частей. При этом для использования этих типов и функций не требуется полного знания того, как именно они написаны - необходимы только описания интерфейсов. Объединение в модули описаний типов данных и функций, предназначенных для работы с ними, со скрытием от пользователя модуля несущественных деталей является дальнейшим развитием структуризации программы.
Все три описанных выше метода повышения абстракции преследуют цель упростить структуру программы, то есть представить ее в виде меньшего количества более крупных блоков и минимизировать связи между ними. Это позволяет управлять большим объемом информации и, следовательно, успешно отлаживать более сложные программы.
Введение понятия класса является естественным развитием идей модульности. В классе структуры данных и функции их обработки объединяются. Класс используется только через его интерфейс - детали реализации для пользователя класса не существенны.
Идея классов отражает строение объектов реального мира - ведь каждый предмет или процесс обладает набором характеристик или отличительных черт, иными словами, свойствами и поведением. Программы в основном предназначены для моделирования предметов, процессов и явлений реального мира, поэтому удобно иметь в языке программирования адекватный инструмент для представления моделей.
Класс является типом данных, определяемым пользователем. В классе задаются свойства и поведение какого-либо предмета или процесса в виде полей данных (аналогично структуре) и функций для работы с ними. Создаваемый тип данных обладает практически теми же свойствами, что и стандартные (примитивные) типы (тип задает внутреннее представление данных в памяти компьютера, множество значений, которое могут принимать величины этого типа, а также операции и функции, применяемые к этим величинам).
Пользовательские типы данных принято называть абстрактными типами данных
Существенным свойством класса является то, что детали его реализации скрыты от пользователей класса за интерфейсом. Интерфейсом класса являются заголовки его открытых методов. Таким образом, класс как модель объекта реального мира является черным ящиком, замкнутым по отношению к внешнему миру.
Конкретные переменные типа данных "класс" называются экземплярами класса, или объектами. Объекты взаимодействуют между собой, посылая и получая сообщения. Сообщение - это запрос на выполнение действия, содержащий набор необходимых параметров. Механизм сообщений реализуется с помощью вызова соответствующих функций.
Создадим класс 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
.
Как упоминалось ранее, классы состоят из двух компонентов: переменных экземпляра и методов. Общая форма метода выглядит следующим образом:
где возвращаемый тип
означает конкретный тип данных, возвращаемый методом. Он может быть любым допустимым типом данных, в том числе и типом созданного класса. Если метод не возвращает значение, то его возвращаемым типом должен быть 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
), также должна быть совместима с возвращаемым типом, указанным для метода.
Инкапсуляция в Java реализована с помощью использования модификаторов доступа.
Язык Java предоставляет несколько уровней защиты, которые позволяет настраивать область видимости данных и методов. В Java имеется четыре категории видимости элементов класса:
private
– члены класса доступны только членам данного класса. Всё что объявлено private
, доступно только конструкторам и методам внутри класса и нигде больше. Они выполняют служебную или вспомогательную роль в пределах класса и их функциональность не предназначена для внешнего пользования. Закрытие (private
) полей обеспечивает инкапсуляцию;
по умолчанию (package-private) – члены класса доступны классам, которые находятся в этом же пакете;
protected
– члены класса доступны классам, находящимся в том же пакете, и подклассам – в других пакетах;
public
– члены класса доступны для всех классов в этом и других пакетах.
Модификатор класса указывается перед остальной частью описания типа отдельного члена класса. Это означает, что именно с него должен начинаться оператор объявления класса.
Когда член класса обозначается модификатором доступа public
, он становится доступным для любого другого кода в программе, включая и методы, определенные в других классах.
Когда член класса обозначается модификатором private
, он может быть доступен только другим членам этого класса. Следовательно, методы из других классов не имеют доступа к закрытому члену класса.
При отсутствии модификатора доступа, члены класса доступны другим членам класса, который находится в этом же пакете.
Модификатор доступа protected
связан с использованием механизма наследования и будет рассмотрен позже.
Модификатор доступа указывается перед остальной частью описания типа отдельного члена класса (то есть, именно с модификатора доступа начинается объявление члена класса).
Член класса (переменная, конструктор, методы), объявленный public
, доступен из любого метода вне класса.
Всё что объявлено private
, доступно только конструкторам и методам внутри класса и нигде больше. Они выполняют служебную или вспомогательную роль в пределах класса и их функциональность не предназначена для внешнего пользования. Закрытие (private
) полей обеспечивает инкапсуляцию.
В подавляющем большинстве случаев, поля класса объявляются как private
(это не касается статических переменных и констант, там ситуация может быть другая). Должны быть веские основания объявить поле класса общедоступным. Манипулирование данными должно осуществляться только с помощью методов.
Для того чтобы дать возможность получить доступ к переменной или дать возможность изменить ее значение, объявляют специальные методы, которые называются "геттерами" и "сеттерами".
Геттер возвращает значение приватного поля, тогда как сеттер меняет значение приватного поля (новое значение передается в качестве аргумента метода).
Хотя сигнатура и имена геттеров и сеттеров могут быть любыми, приучите себя соблюдать строгий шаблон для объявления геттеров и сеттеров.
Геттер должен иметь префикс get
, после которого идет название поля с большой буквы. Геттер, как правило, не имеет входных аргументов.
Сеттер должен иметь префикс set
, после которого идет название поля с большой буквы. Сеттер принимает на вход новое значение поля. Возвращаемый тип, как правило, void
.
Большинство IDE для Java имеют механизм для генерации геттеров и сеттеров. В IntelliJ IDEA нажмите комбинацию Alt+Insert
находясь в окне редактирования java-файла. Откроется контекстное меню Generate, где вы можете выбрать генерацию геттера и сеттера, после чего указать поля, для которых необходимо сгенерировать методы.
Представим, что нам необходимо создать класс «Корзина» (Cart
), который хранит в себе набор объектов класса «Товар» (Item
).
Какие методы «Корзина» должна предоставлять для внешнего использования? Это могут быть, например, методы «Добавить товар», «Убрать последний добавленный товар», «Подсчет суммы цен товаров в корзине», «Повышение цен в корзине на N процентов» и «Снижение цен в корзине на N процентов».
Как вы можете заметить, это публичные методы, а значит, их можно вызвать через оператор-точку имея ссылку ну объект.
Перечень этих публичных методов и составляет интерфейс класса – то есть, с помощью этих методов объект класса будет взаимодействовать с внешним миром.
Эти методы имеют вполне четко определенные входные аргументы и могут возвращать значения четко определенных типов, и никак иначе. По аналогии с этим, поворот колес автомобиля осуществляется четко определенным образом – поворотом руля, и бензин надо заливать в четко определенное отверстие крышки бензобака, а не как-то еще.
То – как будет реализовано хранение товаров в корзине – это внутренняя логика класса и она не должна быть доступна внешнему миру, она должна быть скрыта от внешнего вмешательства. Другие классы, которые будут использовать объекты класса Cart
не должны знать и не должны иметь доступ к тому – как там «внутри» реализовано хранение товаров, подсчет цен и изменение цены на определенный процент и так далее, они могут только лишь использовать предоставленные им публичные методы. Давайте реализуем «Корзину» с помощью структуры «стек», которая, в свою очередь, реализована обычным массивом.
Как мы видим, массив с товарами, указать на вершину стек объявлены как private
члены класса. Это значит, что мы не можем получить к ним доступ извне – они доступны только внутри данного класса.
Программиста, который будет использовать класс Cart
, не должна волновать ситуация с переполнением стека, с попыткой извлечь элемент из пустого стека, он не должен следить за указателем на вершину стека, он даже не должен знать что это стек.
Для него объект класса Cart
это некоторый объект, который предоставляет «услугу» в виде корзины товаров и с этой корзиной можно работать с помощью определенных публичных методов.
В дальнейшем мы можем переделать класс Cart
и поменять внутреннюю реализацию. Мы можем использовать структуру "очередь", мы можем использовать коллекции, мы можем иначе реализовать операции добавления и удаления элемента в стеке, но если мы сохраним интерфейс класса неизменным, то для внешнего мира эти изменения внутренней логики не будут важны и если мы поменяем внутреннюю логику одного небольшого участка программы, то вся остальная программа будет работать так же.
UML – унифицированный язык моделирования (Unified Modeling Language) – это система обозначений, которую можно применять для объектно-ориентированного анализа и проектирования. Его можно использовать для визуализации, спецификации, конструирования и документирования программных систем.
Ссылка на приложения для построения диаграмм UML - http://staruml.io/download
Словарь UML включает три вида строительных блоков:
Диаграммы.
Сущности.
Связи.
Сущности – это абстракции, которые являются основными элементами модели, связи соединяют их между собой, а диаграммы группируют представляющие интерес наборы сущностей.
Диаграмма – это графическое представление набора элементов, чаще всего изображенного в виде связного графа вершин (сущностей) и путей (связей). Язык UML включает 13 видов диаграмм, среди которых на первом месте в списке — диаграмма классов.
Диаграмма классов описывает типы объектов системы и различного рода статические отношения, которые существуют между ними. На диаграммах классов отображаются также свойства классов, операции классов и ограничения, которые накладываются на связи между объектами.
Если кто-нибудь подойдет к вам в темном переулке и спросит: «Хотите посмотреть на диаграмму UML?», знайте – скорее всего, речь идет о диаграмме класса. Большинство диаграмм UML, которые я встречал, были диаграммами классов. - Мартин Фаулер
Графически класс изображается в виде прямоугольника, разделенного на три блока горизонтальными линиями:
имя класса;
атрибуты (свойства) класса;
операции (методы) класса.
Для атрибутов и операций может быть указан один из трех типов видимости:
-
private;
~
без модификатора (default);
#
protected;
+
public.
Видимость для полей и методов указывается в виде левого символа в строке с именем соответствующего элемента.
Каждый класс должен обладать именем, отличающим его от других классов. Имя – это текстовая строка. Имя класса может состоять из любого числа букв, цифр и знаков препинания (за исключением двоеточия и точки) и может записываться в несколько строк.
Атрибут (свойство) – это именованное свойство класса, описывающее диапазон значений, которые может принимать экземпляр атрибута. Класс может иметь любое число атрибутов или не иметь ни одного. В последнем случае блок атрибутов оставляют пустым. Можно уточнить спецификацию атрибута, указав его тип, кратность (если атрибут представляет собой массив некоторых значений) и начальное значение по умолчанию.
Статические атрибуты класса обозначаются подчеркиванием.
Операция (метод) – это реализация метода класса. Класс может иметь любое число операций либо не иметь ни одной. Часто вызов операции объекта изменяет его атрибуты. Графически операции представлены в нижнем блоке описания класса. Допускается указание только имен операций. Имя операции, как и имя класса, должно представлять собой текст. Можно специфицировать операцию, устанавливая ее сигнатуру, включающую имя, тип и значение по умолчанию всех параметров, а применительно к функциям – тип возвращаемого значения.
Абстрактные методы класса обозначаются курсивным шрифтом. Статические методы класса обозначаются подчеркиванием.
Изображая класс, не обязательно показывать сразу все его атрибуты и операции. Для конкретного представления, как правило, существенна только часть атрибутов и операций класса. В силу этих причин допускается упрощенное представление класса, то есть для графического представления выбираются только некоторые из его атрибутов. Если помимо указанных существуют другие атрибуты и операции, вы даете это понять, завершая каждый список многоточием. Чтобы легче воспринимать длинные списки атрибутов и операций, желательно снабдить префиксом (именем стереотипа) каждую категорию в них. В данном случае стереотип – это слово, заключенное в угловые кавычки, которое указывает то, что за ним следует.
Рассмотрим пример класса - его графическое представление и код на языке Java.
Добавьте модификаторы доступа к членам классов, разработанных в лабораторной работе 2 (классы TimeSpan
, BankAccount
и Student
).
Подумайте над тем, какие члены класса сделать приватными, а какие публичными
Для модифицированных классов из второй лабораторной работы нарисуйте диаграммы классов UML.
Необходимо создать класс IntStack
, который реализует функционал стека целых чисел с помощью статического массива.
Нарисуйте класс в диаграмме классов UML
При реализации стека учитывайте следующее:
при реализации класса вам необходимо указать область видимости для полей и методов. Подумайте о том, какие методы необходимо отнести к "интерфейсу", а какие к реализации;
размер стека для внешнего пользователя не имеет значения и теоретически неограничен;
внешний пользователь не должен знать и догадываться, что стек "внутри" реализован с помощью обычного статического массива;
вы должны написать "реализацию" класса таким образом, чтобы можно моделировать динамическое поведение стека с помощью обычного статического массива;
продумайте, какие аргументы методы должны принимать и какие значения возвращать.
ВАЖНО! Стек необходимо реализовать с помощью статического целочисленного массива!
НЕ ИСПОЛЬЗУЙТЕ КОЛЛЕКЦИИ И ПОТОКИ ДАННЫХ, ТОЛЬКО МАССИВЫ
Для внешнего пользователя класса, поведение класса выглядит следующим образом:
пользователь может создать новый стек с конструктором без параметров;
пользователю доступна команда pop()
для извлечения элемента из стека. Метод pop()
возвращает извлеченный элемент. При попытке извлечь из пустого стека, метод возвращает 0
или выбрасывает исключение (реализуйте выброс исключения, если вы знаете, как это делать и что такое исключение);
пользователю доступна команда push()
, которая добавляет элемент в стек;
пользователю доступна команда peek()
для просмотра элемента на вершине стека без его исключения.
пользователь может получить значение количества элементов в стеке с помощью метода size()
;
пользователь может проверить, пустой ли стек с помощью метода isEmpty()
;
пользователь может очистить стек полностью с помощью метода clear()
.
Создайте Java-классы исходя из диаграммы, представленной ниже. Код методов реализовывать не нужно, только заголовки и общую структуру классов.
Возможность повторного использования кода принадлежит к числу важнейших преимуществ языков объектно-ориентированного программирования.
композиция (composition) - объекты уже имеющихся классов просто создаются внутри нового класса. Программист просто использует функциональность уже готового кода;
наследование (inheritance) - новый класс создается как специализация уже существующего класса. Взяв существующий класс за основу, вы добавляете к нему свой код без изменения существующего класса.
Композиция - использование функционала одних объектов в составе других объектов. Рассмотрим пример класса FileManager
, в котором определен метод для сохранения текстовых данных в файл. Класс Document
использует функционал класса FileManager
, чтобы сохранить текстовый документ на жесткий диск.
Наследование - отношение между классами, в котором один класс повторяет структуру и поведение другого класса (или нескольких других классов).
Класс, на основе которого создается новый класс, называется суперклассом (базовым классом, родительским классом). Новый создаваемый класс называется подклассом (дочерним классом, производным классом, классом-наследником и так далее).
Создание подкласса практически не отличается от создания обычного класса, кроме необходимости указать суперкласс, на основе которого создается подкласс. В Java для этого существует ключевое слово extends:
В Java, в отличие от C++, отсутствует множественное наследование, то есть подкласс может создаваться на основе только одного суперкласса.
В Java присутствует многоуровневое наследование: подкласс может быть суперклассом для другого класса. Благодаря этому можно создавать целые цепочки классов, связанные механизмом наследования
Если член класса определен как private
, то при наследовании доступ к нему со стороны подкласса закрыт. Важно понимать, что приватный член суперкласса в подклассе есть, только он закрыт для прямого доступа. К примеру, данный код не скомпилируется
Закрытыми могут быть как поля класса, так и его методы. Если необходимо открыть поля или методы для доступа к ним со стороны подкласса, при объявлении членов суперкласса используют слово protected
либо создают геттеры и сеттеры для доступа к полям. К примеру, данный код скомпилируется и будет работать корректно
Данный пример демонстрирует доступ к полям с помощью геттеров
Так как в наследовании участвуют два класса, базовый и производный, не сразу понятно, какой же объект получится в результате. Внешне все выглядит так, словно новый класс имеет тот же интерфейс, что и базовый класс, плюс еще несколько дополнительных полей и методов.
Однако наследование не просто копирует интерфейс базового класса. Когда вы создаете объект производного класса, внутри него содержится подобъект базового класса. Этот подобъект выглядит точно так же, как выглядел бы созданный обычным порядком объект базового класса. Поэтому извне представляется, будто бы в объекте производного класса "упакован" объект базового класса.
Чтобы подобъект базового класса был правильно инициализирован, при вызове конструктора подкласса, сначала вызывается конструктор базового класса, у которого есть необходимые знания и привилегии для проведения инициализации базового класса.
При использовании конструкторов без параметров, у компилятора не возникает проблем с вызовом таких конструкторов, так как нет нужды передавать аргументы. В этом случае Java автоматически вставляет вызовы конструктора базового класса в конструктор производного класса.
Результат работы такого приложения будет следующим
Как видно из данного примера, цепочка вызовов конструкторов начинается с самого базового класса. Таким образом, подобъект базового класса инициализируется еще до того, как он станет доступным для конструктора производного класса. Даже если конструктор класса Cat
не будет определен, Java сгенерирует конструктор по умолчанию, в котором также будет вызван конструктор базового класса.
Если в классе не определен конструктор без параметров, то вызов конструктора базового класса надо будет оформлять явно. К примеру, такой код вызовет ошибку на этапе компиляции
Для явного вызова конструктора суперкласса используется ключевое слово super. Более подробно мы рассмотрим его ниже, а сейчас приведем пример корректного вызова конструктора суперкласса
Как было сказано ранее, наследование в Java реализуется следующим образом - в объект производного класса добавляется скрытый объект базового класса, который и обеспечивает вызов методов суперкласса.
Ключевое слово super
как раз и ссылается на этот скрытый объект суперкласса. Используя это ключевое слово, можно получить доступ к членам суперкласса (если позволяет их модификатор доступа)
Как видно из примера, ключевое слово super
имеет что-то общее с ключевым словом this
.
При использовании механизма наследования возникает проблема с использованием методов суперкласса. Часто метод суперкласса не отражает изменения и нововведения, внесенные в подклассе и вызов таких методов дает некорректную информацию об объекте. Рассмотрим следующий пример
Результат работы такого приложения будет следующим
Как вы уже понимаете, в данной части кода
был вызван метод 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
.
Следует отличать механизм перегрузки метода от механизма переопределения метода.
Метод может быть как перегруженным, так и переопределенным.
Иногда бывает необходимо запретить наследоваться от какого-то класса либо запретить переопределять метод. В этом случае, в объявлении класса или метода укажите ключевое слово final
В Java определен специальный класс Object
, который является суперклассом для всех классов Java. Иными словами, все классы в языке Java являются подклассами, производными от класса Object
.
В классе Object определены перечисленные ниже методы, которые доступны в любом объекте
С некоторыми методами класса Object
мы встретимся позже, а сейчас нам интересен только метод toString()
.
Метод toString()
призван возвращать строковое представление объекта (список значений полей). Рассмотрим пример
Обратите внимание, что класс Box
не содержит никаких членов, однако, так как Box наследуется от класса Object
, ему доступны методы суперкласса и метод toString()
в частности.
Результатом работы данного примера является следующая строка
Метод toString()
в классе Object
выводит полное название класса и 16-ричное представление хеш-кода объекта.
Если вам необходимо вывести значения полей объекта в виде строки, используйте метод toString()
, он для этого и был предназначен, его использование является общепринятым правилом.
Метод getInfo()
в примере выше был использован только в демонстрационных целях!
Чтобы метод toString()
выводил нужную информацию, его необходимо пепреопределить как в следующем примере
Результат будет следующим
Также метод toString()
имеет одну примечательную особенность - его можно явно не вызывать, а просто указывать ссылочную переменную, Java сама вызовет метод toString()
. Например, следующий код
выдаст следующий результат
Как вы видите, результат работы строк 5 и 6 является идентичным.
Отношение обобщения (generalization) в UML изображается при помощи сплошной линии и жирной треугольной стрелки, ведущей от подкласса с суперклассу.
Что обозначает это отношение? В спецификации UML по этому поводу сказано
Обобщение - таксономическое отношение между более общим элементом и более конкретным. Каждый экземпляр конкретного элемента также является непрямым экземпляром обобщенного элемента. Таким образом, конкретизированный элемент косвенно обладает свойствами обобщенного элемента
Является ли отношение обобщения синонимом наследования (inheritance) в объектно-ориентированном проектировании? Ответ зависит от области применения отношения. Для диаграммы концептуальных классов из модели предметной области ответом будет "нет". В этом случае отношение обобщения подразумевает то, что суперкласс является множеством, а подкласс - подмножеством. С другой стороны, на диаграмме классов проектирования это отношение подразумевает объектно-ориентированное наследование свойств подкласса от суперкласса.
Композиция (composition), так же известная как композитная агрегация (composite aggregation), является строго определенным типом связи "целое-часть" и полезна в некоторых моделях. Отношение композиции предполагает, что
экземпляр части (например, Wheel
) в каждый момент времени принадлежит только одному целому предмету (например, Car
);
часть всегда принадлежит целому (пальцы не существуют отдельно от руки);
целое ответственно за создание и удаление своих частей - либо через самостоятельное создание\удаление, либо через взаимодействие с другими объектами. Следствием этих ограничений является то, что при уничтожении композитного объекта его части должны быть либо уничтожены, либо присоединены в другому композитному объекту. Например, если реальная настольная игра "Монополия" уничтожается, то также уничтожаются все ее клетки (с концептуальной точки зрения). Аналогично, если программный объект Car
уничтожается, то уничтожаются и программные объекты Wheel
.
Для обозначения композиции в UML используется закрашенный ромб на линии ассоциации со стороны целого.
Вернитесь к классу TimeSpan
из второй лабораторной работы.
Используя механизм перегрузки методов, допишите или исправьте методы так, чтобы класс имел следующий функционал:
конструктор без аргументов (в этом случае временной интервал равен 0 часов и 0 минут);
конструктор с 1 аргументом (минуты);
конструктор с 2 аргументами (часы и минуты);
конструктор с 1 аргументом типа TimeSpan
(из входящего TimeSpan
считываются часы и минуты для создания нового TimeSpan
);
метод добавления времени ко временному интервалу с 2 аргументами (часы и минуты);
метод добавления времени ко временному интервалу с 1 аргументом (минуты);
метод добавления времени ко временному интервалу с 1 аргументом типа TimeSpan
(из входящего TimeSpan
считываются часы и минуты для добавления к текущему);
метод вычитания времени из текущего временного интервала с 2 аргументами (часы и минуты);
метод вычитания времени из текущего временного интервала с 1 аргументом (минуты);
метод вычитания времени из текущего временного интервала с 1 аргументом типа TimeSpan
(из входящего TimeSpan
считываются часы и минуты для вычитания из текущего).
1. Создайте класс Person
, который должен содержать конструктор, геттеры\сеттеры и следующие поля:
фамилия;
имя;
возраст.
Переопределите метод toString()
, который должен возвращать строку следующего формата:
2. Создайте класс Student
, который должен наследоваться от класса Person
. Добавьте конструктор, геттеры\сеттеры и следующие поля:
группа;
номер студенческого билета.
Переопределите метод toString()
, который должен возвращать строку следующего формата:
3. Создайте класс Lecturer
, который должен наследоваться от класса Person
. Добавьте конструктор, геттеры\сеттеры и следующие поля:
кафедра;
зарплата.
Переопределите метод toString()
, который должен возвращать строку следующего формата:
4. Используя восходящее преобразование, создайте в классе Main
несколько объектов классов Student
и Lecturer
, после чего создайте массив, который бы мог включать объекты классов Person
, Student
, Lecturer
. Заполните массив объектами этих классов.
5. Используя цикл, обратитесь к элементам массива и выведите в консоль, с помощью метода toString()
, информацию от каждого объекта.
В данной задаче мы допишем игру "Камень Ножницы Бяумага".
Скачайте, распакуйте и откройте проект в IntelliJ IDEA, запустите приложение и изучите исходный код;
с помощью механизма полиморфизма создайте классы игровых фигур (Камень, Ножницы, Бумага), которые наследуются от GameShape
;
Напишите логику метода generateShape()
, который возвращает одну из трех случайных фигур (объект одного из трех классов);
в методе actionPerformed()
допишите создание объекта игровой фигуры в зависимости от того, на какую кнопку нажал пользователь (читайте комментарии внутри метода);
реализуйте метод checkWinner()
, который принимает на вход два объекта игровых фигур - фигура компьютера и игрока. С помощью оператора instanceof
выясните, фактический объект какого класса скрывается за ссылочной переменной типа GameShape
. В зависимости от сочетания фигур, метод возвращает: 1 если выиграл игрок, -1 если выиграл компьютер или 0 если ничья;
Сыграйте в игру и убедитесь, что ваш код корректен и игра работает правильно.
Скачайте архив с проектом
Данная программа позволяет рисовать простые фигуры. С помощью кнопок на верхней панели выбираете нужную фигуру, после чего в поле рисования зажимаете кнопку мыши и рисуете фигуру нужных размеров.
Откройте проект, прочитайте комментарии, разберитесь как работает приложение;
Используя механизм полиморфизма, модифицируйте программу, добавив возможность выбирать и рисовать эллипсы;
Задание на дополнительный балл. Добавьте в программу кнопку Clear, по нажатию на которую программа должна стирать все фигуры.
При сдаче лабораторной работы, вы должны быть готовыми показать, где и для каких целей используется полиморфизм и восходящее преобразование.
В данной лабораторной работе задания выполняются с помощью плагина EduTools!
При решении задачи сначала добавьте сигнатуру методов и возвращаемые значения для того, чтобы тесты могли скомпилироваться корректно!
Если вы обновили IntelliJ IDEA, то используйте данный архив для выполнения лабораторной работы
Реализуйте каждую задачу с помощью механизма наследования, а потом - с помощью механизма композиции.
Для каждой реализации нарисуйте UML-диаграмму классов. Вы можете нарисовать одну диаграмму для двух заданий, реализованных с помощью наследования и одну диаграмму для заданий, реализованных с помощью механизма наследования.
Дан класс Point
, который моделирует точку в двумерном пространстве. Класс включает в себя следующие конструкторы и публичные методы:
Создайте класс Point3D
, который расширяет класс Point через наследование. Он должен вести себя как Point
, за исключением того что это должна быть точка в трехмерном пространстве, которая хранит значение координаты Z
.
Вы должны предоставить те же методы, что и суперкласс, а также реализовать дополнительное поведение
Класс Point3D()
должен переопределить требуемые методы, чтобы они работали корректно с учетом третьей координаты. Также класс Point3D
должен вести себя иначе в следующих ситуациях:
при вызове метода setLocation(int x, int y)
, координата z
должна быть выставлена в 0;
при вызове метода toString()
, строка должна выводить три координаты, а не две;
Дан класс 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 и все товары учтены по их полной стоимости.
метод distanceFromOrigin()
должны учитывать координату z и возвращать расстояние по формуле .
Название метода
Описание метода
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()
для получения значений часов и минут).
getTotalHours()
Возвращает количество часов в промежутке в виде дробного числа. Например, если временной интервал 9 часов 45 минут, то метод должен вернуть 9.75
.
getTotalMinutes()
Возвращает количество минут в текущем промежутке времени (помните, что в интервале есть еще показатель часов, который нужно умножить на 60)
Название метода
Описание метода
Student(name, year)
Конструктор. Входные аргументы – имя и год обучения
addCourse(courseName)
Добавляет дисциплину(в формате String) к массиву дисциплин студента
dropAll()
Удаляет все дисциплины студента
getCourseCount()
Возвращает количество дисциплин, которые студент изучает
getName()
Возвращает имя студента
getTuition()
Возвращает количество денег, которые студент заплатил за обучение (с условием, что каждый год обучения обошелся студенту в 1000 гривен)
getYear()
Возвращает год обучения студента (от 1 до 4)
Название метода
Описание
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)
Снизить цены товаров в корзине на определенный процент (значение процента передается как аргумент метода).
Метод | Описание |
| Создает новый объект, аналогичный клонируемому объекту |
| Определяет равнозначность объектов |
| Вызывается перед тем, как неиспользуемый объект будет удален "сборщиком мусора" |
| Определяет класс объекта во время выполнения |
| Возвращает хеш-код, связанный с вызывающим объектом |
| Возобновляет работу потока, ожидающего уведомления от вызывающего объекта |
| Возобновляет работу всех потоков, ожидающих уведомления от вызывающего объекта |
| Возвращает символьную строку, описывающую объект |
| Ожидает исполнения другого потока |
Сигнатура | Описание |
| Создает точку с координатами |
| Создает точку с координатами |
| Устанавливает новые координаты |
| Возвращает расстояние от текущей точки до входной точки по формуле расстояния Евклида с учетом координаты |
| Возвращает координату |
Сигнатура | Описание |
| Создает объект |
| Добавляет товар в чек |
| Возвращает итоговую стоимость товаров |
| Распечатывает список товаров |
Сигнатура | Описание |
| Возвращает стоимость товара |
| Возвращает скидку для этого товара |
Сигнатура | Описание |
| Создает объект |
| Возвращает количество товаров со скидкой |
| Возвращает общую скидку в гривнах |
| Возвращает процент скидки для товаров (на сколько процентов покупатель заплатил меньше). Процент можно посчитать по формуле: 100 - (цена со скидкой * 100) / полная цена |
Сигнатура | Описание |
| Создает точку с координатами |
| Создает точку с координатами |
| Устанавливает новые координаты точки |
| Возвращает значение координаты X |
| Возвращает значение координаты Y |
| Возвращает строку в виде |
| Возвращает расстояние от текущей точки до входной точки по формуле расстояния Евклида |
|
Абстрактный класс - это класс, который нельзя реализовать непосредственно. Вместо этого создается экземпляр подкласса. Обычно абстрактный класс имеет одну или более абстрактных операций. У абстрактной операции нет реализации, это чистое объявление, которые клиенты могут привязать к абстрактному классу.
Наиболее распространенным способом обозначения абстрактного класса или операции в UML является написание их имен курсивом. Можно также сделать свойства абстрактными, определяя абстрактное свойство или методы доступа. Если курсив сложно изобразить (например, если вы рисуете на доске), можно прибегнуть к метке {abstract}.
Для указания класса как абстрактного, выделите класс и выделите чекбокс isAbstract.
Для указания метода как абстрактного, выделите метод и выделите чекбокс isAbstract.
Интерфейс - это класс, не имеющий реализации, то есть вся его функциональность абстрактна. Интерфейсы прямо соответствуют интерфейсам в Java или C# и являются общей идиомой в других типизированных языках. Интерфейс обозначается стереотипом (ключевым словом) <<interface>>.
По умолчанию, при добавлении интерфейса на диаграмму классов, он выглядит как кружок с названием интерфейса.
Для того, чтобы преобразовать его в классический интерфейс согласно стандарту UML, выделите интерфейс и в панели Editors выберите Format -> Label.
После этого снимите выделение с пункта "Suppress Operations"
Графическое представление в формате UML
Исходный код
Нарисуйте диаграммы классов UML для задания 1 по абстрактным классам и для заданий 1, 2, 4 по интерфейсам.
Дан следующий абстрактный класс
Создайте класс Dog
как подкласс Animal
и реализуйте метод makeSound()
. Создайте класс Cat
как подкласс Animal
и реализуйте метод makeSound()
. Создайте в методе main()
объекты каждого из подклассов и вызовите метод makeSound()
.
Напишите исходный код классов, приведенных в диаграмме классов UML (методы реализовывать не нужно).
Напишите исходный код класса Point
, который реализует следующий интерфейс
Напишите исходный код класса Smartphone
, который реализует следующие интерфейсы
Напишите исходный код интерфейса, приведенного в диаграмме классов UML. Напишите исходный код класса DynamicIntArrayImpl
, реализующего этот интерфейс (методы реализовывать не нужно)
Задание на дополнительные баллы. Реализуйте методы класса DynamicIntArrayImpl
, чтобы получился рабочий класс. Проверьте корректность его работы.
При вставке элемента, массив должен "раздвигаться", размер массива увеличивается. При вставке элемента по индексу, старый элемент с этим индексом смещается вправо.
При удалении элемента, массив должен "сужаться", размер массива уменьшается.
Найдите в третьей лабораторной работе задание "Класс IntStack
". Исходя из условий задания, разработайте интерфейс Stack
с перечнем методов, которые, на ваш взгляд, нужно внести в интерфейс.
Дан следующий класс
Сделайте так, чтобы класс реализовывал интерфейс Comparable
. Напишите исходный код метода compareTo()
. Логика метода следующая:
сначала сравнивается цена автомобилей, "больше" тот автомобиль, у которого меньше цена;
если цены равны, то сравнивается год выпуска автомобиля, "больше" тот автомобиль, который меньше по возрасте;
если возраст одинаковый, то автомобили сравниваются по количеству лошадиных сил. "Больше" тот автомобиль, у которого лошадиных сил больше.
Не используйте типизированный Comparable
! Для сравнения используйте только арифметические операторы!
Скачайте архив с проектом по этому адресу - ссылка.
Данная программа выводит список студентов и позволяет отсортировать их по имени, фамилии и по среднему баллу.
В программе уже реализована сортировка студентов по имени.
Задание состоит в следующем:
доделать программу, чтобы по нажатию на соответствующие кнопки происходила сортировка студентов по фамилии и по среднему баллу;
задание на дополнительные баллы - доработайте программу так, чтобы при повторном нажатии на ту же кнопку сортировки, осуществлялась обратная сортировка студентов. То есть, при первом нажатии на кнопку "Сортировка по имени" студенты сортировались в прямом порядке (по возрастанию), а при повторном нажатии на эту же кнопку, студенты сортировались в обратном порядке (по убыванию).
Список студентов находится в коллекции students
, для сортировки необходимо вызвать у коллекции метод sort()
и в этот метод передать объект компаратора.
Что такое компаратор. Базовая операция для почти любой сортировки – сравнение двух элементов. Если вы сортируете обычные числа или строки, сравнение происходит элементарно по известным всем правилам. Но как быть, если вы хотите отсортировать объекты, которые могут иметь десятки полей различных типов?
Для этого существует так называемый компаратор. Компаратор в Java – это объект класса, который реализует интерфейс Comparator
. Интерфейс определяет всего один метод compare()
, который принимает на вход два объекта типаObject
. Если первый объект «меньше» – метод возвращает отрицательное число (обычно это просто -1, но может быть и любое другое отрицательное число), если первый объект «больше» – метод возвращает положительное число (обычно это 1, но может быть и любое другое положительное число), если объекты «равны» – метод возвращает 0.
Задача программиста - прописать нужную логику сравнения и вернуть -1/0/1 в том или ином случае.
Вы создаете класс, указываете, что этот класс реализует интерфейс Comparator
и в методе compare()
описываете логику сравнения двух объектов. В случае со сравнением двух студентов по имени, код компаратора выглядит следующим образом
Обратите внимание, что сначала мы должны убедиться, что оба объекта являются объектами класса Student
, после чего делаем нисходящее преобразование.
В данном случае мы сравниваем два поля name
у двух объектов класса Student
. Так как это тип String
, нам нет нужды сравнивать строки «вручную», мы просто можем воспользоваться методом compareTo()
, который есть у любого объекта класса String
.
Возвращает расстояние от начала координат (0, 0) до точки по формуле расстояния Евклида