← Назад к вопросам

От чего наследовал исключения?

1.7 Middle🔥 121 комментариев
#Другое

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Иерархия наследования исключений в Java

При проектировании собственных исключений выбор базового класса критически важен для правильной обработки ошибок. Моя стратегия следующая:

Наследование от Exception vs RuntimeException

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

// Наследую от Exception (checked исключение)
public class InsufficientFundsException extends Exception {
    public InsufficientFundsException(String message) {
        super(message);
    }
}

// Используется для предсказуемых ошибок
public class BankAccount {
    public void withdraw(BigDecimal amount) throws InsufficientFundsException {
        if (balance.compareTo(amount) < 0) {
            throw new InsufficientFundsException("Недостаточно средств");
        }
        balance = balance.subtract(amount);
    }
}

Checked исключения (extends Exception)

Использую Exception для исключений, которые:

  • Предсказуемы и ожидаемы — например, файл не найден, недостаточно средств на счёте
  • Должны быть обработаны вызывающим кодом — компилятор заставляет это сделать
  • Представляют бизнес-логику ошибок — это часть контракта метода
// Checked исключение для бизнес-логики
public class PaymentFailedException extends Exception {
    private final PaymentErrorCode errorCode;
    
    public PaymentFailedException(String message, PaymentErrorCode code) {
        super(message);
        this.errorCode = code;
    }
    
    public PaymentErrorCode getErrorCode() {
        return errorCode;
    }
}

Unchecked исключения (extends RuntimeException)

RuntimeException использую для:

  • Программных ошибок — нарушение инвариантов, неправильное использование API
  • Непредсказуемых сбоев — проблемы с БД, сетью, когда обработка в каждом месте неэффективна
  • Критических ошибок, от которых приложение не может восстановиться
// Unchecked исключение для программных ошибок
public class InvalidProductStateException extends RuntimeException {
    public InvalidProductStateException(String message) {
        super(message);
    }
}

// Используется в методе без throws
public class Product {
    public void activate() {
        if (status == ProductStatus.DELETED) {
            throw new InvalidProductStateException(
                "Не можно активировать удалённый товар"
            );
        }
        status = ProductStatus.ACTIVE;
    }
}

Собственная иерархия для доменного слоя

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

// Базовое checked исключение для бизнес-ошибок
public abstract class DomainException extends Exception {
    protected DomainException(String message) {
        super(message);
    }
}

// Конкретные бизнес-исключения
public class OrderNotFoundException extends DomainException {
    public OrderNotFoundException(UUID orderId) {
        super("Заказ не найден: " + orderId);
    }
}

public class InvalidOrderStateException extends DomainException {
    public InvalidOrderStateException(String state) {
        super("Недопустимое состояние заказа: " + state);
    }
}

Исключения инфраструктурного слоя

Для проблем с внешними системами использую RuntimeException:

// Unchecked для ошибок инфраструктуры
public class DatabaseException extends RuntimeException {
    public DatabaseException(String message, Throwable cause) {
        super(message, cause);
    }
}

public class ApiClientException extends RuntimeException {
    private final int statusCode;
    
    public ApiClientException(String message, int statusCode, Throwable cause) {
        super(message, cause);
        this.statusCode = statusCode;
    }
}

Правило большого пальца

Если ошибка — это бизнес-событие, которое нужно обработатьException (checked).

Если ошибка — это баг или сбой инфраструктурыRuntimeException (unchecked).

Какое основание NOT выбирать

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

Не создаю иерархии глубже 2-3 уровней — избыточная сложность, нарушает KISS.

Вывод

Мойсилыподход основан на том, что checked исключения заставляют обработать предсказуемые ошибки, а unchecked исключения используются для непредсказуемых сбоев. Это делает код более надёжным и явным в своём намерении.

От чего наследовал исключения? | PrepBro