(проект лабораторной будет меняться по итогу принятия лабораторной 3)
Лабораторная работа № 4 — «Исключения, диагностика и устойчивое поведение программы»
С этого момента студент перестаёт считать, что «ошибка = программа упала».
Перед работой преподаватель выдаёт версию WalletService, в которой присутствуют типовые проблемные ситуации:
сумма <= 0 некорректная строка с числом неизвестный кошелёк недостаточно средств неверная дата неожиданная внутренняя ошибка
Java различает checked exceptions, которые должны быть объявлены/обработаны согласно правилам компилятора, и unchecked exceptions — наследников RuntimeException; конструкция try может сочетаться с catch и finally.
Студент реализует два пользовательских исключения:
public class InsufficientFundsException extends Exception { ... }
и:
public class InvalidTransactionException extends RuntimeException { ... }
Это специально сделано так, чтобы checked/unchecked не изучались как два определения из конспекта. Студент должен почувствовать разницу через API:
public void transfer(...) throws InsufficientFundsException { ... }
и:
throw new InvalidTransactionException(...);
Практический сценарий:
1. пользователь вводит сумму; 2. строка преобразуется в BigDecimal; 3. сервис пытается выполнить перевод; 4. разные виды ошибок обрабатываются на соответствующем уровне; 5. приложение не завершается после исправимой пользовательской ошибки.
Требуется применить try/catch, несколько обработчиков, throw, throws и один небольшой эксперимент с finally.
Пример каркаса:
try { BigDecimal amount = new BigDecimal(input); walletService.transfer(source, target, amount); } catch (NumberFormatException e) { // сообщение о неверном формате } catch (InsufficientFundsException e) { // корректное сообщение пользователю }
Отдельное задание — диагностика. Вместо:
catch (Exception e) { }
и вместо повсеместного:
e.printStackTrace();
студент добавляет логирование на уровне приложения. Конкретную библиотеку в этой работе можно не навязывать: для базовой версии достаточно стандартного логгера Java; позже Spring Boot даст готовую logging-инфраструктуру.
Студенту выдаются три антипаттерна:
catch (Exception e) { }
catch (Exception e) {
return null;
}
catch (Throwable t) {
// продолжить работу
}
и требуется письменно объяснить, какую информацию они уничтожают и на каком уровне следует обрабатывать исключение.
Практическая ловушка: метод низкого уровня не должен выводить пользователю "У вас ошибка!". Он либо возвращает результат, либо выбрасывает осмысленное исключение; преобразование ошибки в текст для пользователя происходит в UI-слое. Тем самым продолжается SRP.
Обязательный результат: пользователь может выполнить несколько операций подряд; неверный ввод не убивает приложение; перевод с недостаточным балансом не изменяет состояние кошельков; исключение содержит содержательное сообщение; причина исходной ошибки при необходимости сохраняется через chaining.
Автопроверка: assert-подобные преподавательские проверки на недостаточный баланс, отрицательную сумму, нулевую ссылку, корректность состояния после неудачной операции и отсутствие catch (Exception) {}.
Защита: иерархия Throwable → Exception → RuntimeException; роль Error; checked/unchecked; throw vs throws; порядок catch; finally; где следует перехватывать исключение; почему нельзя использовать исключение как обычный if; зачем собственный класс исключения; что теряется при пустом catch.
Воппросы для углубления: Что такое исключения и как устроена их иерархия
в Java?Checked vs unchecked: в чем разница и как
это влияет на проектирование?Как работают try-
catch-finally, throw и throws?Зачем создавать
пользовательские исключения и какие правила для
них важны?Какую роль играют логирование и
диагностика ошибок?
