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

Когда какое исключение нужно создать?

2.0 Middle🔥 121 комментариев
#Основы Java

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

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

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

Стратегия выбора исключений в Java

Основной принцип

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

Иерархия исключений

Throwable
├── Error (критичные, не ловим обычно)
│   ├── OutOfMemoryError
│   ├── StackOverflowError
│   └── VirtualMachineError
└── Exception
    ├── Checked Exceptions (ОБЯЗАТЕЛЬНО ловить или объявлять)
    │   ├── IOException
    │   ├── SQLException
    │   ├── FileNotFoundException
    │   └── ParseException
    └── Unchecked Exceptions / RuntimeException (необязательно ловить)
        ├── NullPointerException
        ├── IllegalArgumentException
        ├── IllegalStateException
        ├── IndexOutOfBoundsException
        └── ArithmeticException

Checked vs Unchecked Exceptions

public class ExceptionTypes {
    // Checked Exception - ОБЯЗАТЕЛЬНО ловим или объявляем
    public void readFile(String filename) throws IOException {
        FileReader reader = new FileReader(filename);
        // Если файл не найден - IOException
    }
    
    // Unchecked Exception - можем не ловить
    public int divide(int a, int b) {
        // Если b == 0 - ArithmeticException (не обязательно объявлять)
        return a / b;
    }
    
    public void processArray(int[] arr, int index) {
        // Если index >= arr.length - ArrayIndexOutOfBoundsException
        int value = arr[index];
    }
}

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

// ✅ Создавай свои исключения, если нужна специфичность
public class UserNotFoundException extends RuntimeException {
    private Long userId;
    
    public UserNotFoundException(Long userId) {
        super("User with id " + userId + " not found");
        this.userId = userId;
    }
    
    public Long getUserId() {
        return userId;
    }
}

// ❌ Плохо: используешь generic Exception
public User getUserById(Long id) throws Exception {
    if (id == null) {
        throw new Exception("ID is null");
    }
    // ...
}

// ✅ Хорошо: используешь специфичное исключение
public User getUserById(Long id) {
    if (id == null) {
        throw new IllegalArgumentException("ID cannot be null");
    }
    // ...
}

Таблица выбора исключений

Для валидации входных параметров:

public void setAge(int age) {
    if (age < 0 || age > 150) {
        // IllegalArgumentException - для некорректного аргумента
        throw new IllegalArgumentException("Age must be between 0 and 150");
    }
    this.age = age;
}

public void process(List<String> items) {
    if (items == null) {
        // NullPointerException - явно проверяем null
        throw new NullPointerException("items cannot be null");
    }
    // ...
}

Для состояния объекта:

public class BankAccount {
    private boolean closed = false;
    
    public void withdraw(BigDecimal amount) {
        if (closed) {
            // IllegalStateException - объект в неправильном состоянии
            throw new IllegalStateException("Account is closed");
        }
        // ...
    }
}

public class PaymentProcessor {
    private State state = State.IDLE;
    
    public void process(Payment payment) {
        if (state != State.READY) {
            throw new IllegalStateException(
                "Cannot process payment in state: " + state
            );
        }
    }
}

Для работы с коллекциями и массивами:

public void accessElement(List<String> list, int index) {
    if (index < 0 || index >= list.size()) {
        // IndexOutOfBoundsException - неправильный индекс
        throw new IndexOutOfBoundsException(
            "Index: " + index + ", Size: " + list.size()
        );
    }
    // ...
}

Для операций, которые не поддерживаются:

public class ReadOnlyList<E> extends AbstractList<E> {
    @Override
    public E set(int index, E element) {
        // UnsupportedOperationException - операция не поддерживается
        throw new UnsupportedOperationException("This list is read-only");
    }
}

Checked Exceptions - когда использовать?

// Используй Checked для ошибок, которые НУЖНО обработать
public class FileProcessor {
    // IOException - known issue, клиент ДО ЛЖЕ его обработать
    public void processFile(String path) throws IOException {
        try (FileReader reader = new FileReader(path)) {
            // read file
        }
        // Если файла нет - IOException обязательно обработаем выше
    }
}

// Используй Checked для ошибок внешних систем
public class DatabaseService {
    // SQLException - обязательно нужно обработать
    public User getUserById(Long id) throws SQLException {
        try (Connection conn = getConnection()) {
            // execute query
        }
    }
}

Custom Exception - Best Practices

// ✅ Хорошо: наследуем RuntimeException для unchecked
public class ProductNotFoundException extends RuntimeException {
    private final String productCode;
    
    public ProductNotFoundException(String productCode) {
        super("Product not found: " + productCode);
        this.productCode = productCode;
    }
    
    public String getProductCode() {
        return productCode;
    }
}

// Используем:
public Product findProduct(String code) {
    return productRepository.findByCode(code)
        .orElseThrow(() -> new ProductNotFoundException(code));
}

// ✅ Если нужна обработка - наследуем от Exception
public class PaymentFailedException extends Exception {
    private final PaymentStatus status;
    private final BigDecimal amount;
    
    public PaymentFailedException(PaymentStatus status, BigDecimal amount) {
        super("Payment failed with status: " + status);
        this.status = status;
        this.amount = amount;
    }
    
    public PaymentStatus getStatus() {
        return status;
    }
    
    public BigDecimal getAmount() {
        return amount;
    }
}

Praktical Guide - Таблица решений

СитуацияИсключениеПример
Аргумент null или некорректенIllegalArgumentExceptionthrow new IllegalArgumentException("Email is invalid");
Объект в неправильном состоянииIllegalStateExceptionthrow new IllegalStateException("Connection closed");
Индекс вне границIndexOutOfBoundsExceptionавтоматически при list.get(999)
Операция не поддерживаетсяUnsupportedOperationExceptionthrow new UnsupportedOperationException();
Файл не найденFileNotFoundExceptionnew FileReader("missing.txt") throws
БД ошибкаSQLExceptionловим из JDBC
Парсинг JSON/XML провалилсяParseException / JsonSyntaxExceptionсоздаём свой custom exception
Ресурс не найден (service layer)NotFoundException (custom)наследуем RuntimeException
Конфликт данныхConflictException (custom)наследуем RuntimeException
Бизнес-правило нарушеноBusinessException (custom)наследуем RuntimeException или Exception

Вывод

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

  • Валидация параметровIllegalArgumentException
  • Состояние объектаIllegalStateException
  • Специфичные бизнес-ошибки → создай свой RuntimeException
  • Ошибки, которые нужно обработать → checked Exception
  • Не используй generic Exception или Throwable