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

Что такое Java Exceptions?

2.0 Middle🔥 181 комментариев
#SOLID и паттерны проектирования#Spring Boot и Spring Data

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

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

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

Java Exceptions: механизм обработки ошибок

Java Exceptions — это объектно-ориентированный механизм для обработки ошибок и исключительных ситуаций. В отличие от других языков (например, C), которые возвращают error codes, Java использует exceptions для сигнализации об ошибках и их обработки в специальных catch блоках.

Иерархия Exceptions в Java

                    Throwable
                   /          \
              Error           Exception
            /     |     \       /         \
           ...  OutOfMemory  ...   IOException  RuntimeException
                             |         |         /      |       \
                             |         |    NullPointerException  ...
                             |         |    ArrayIndexOutOfBoundsException
                             |         |    ClassCastException
         FileNotFoundException

Три типа Throwables:

  1. Error — серьёзные проблемы, которые приложение обычно не может обработать

    • OutOfMemoryError
    • StackOverflowError
    • VirtualMachineError
  2. Checked Exceptions (extends Exception, но не RuntimeException)

    • IOException, SQLException, ClassNotFoundException
    • Компилятор ЗАСТАВЛЯЕТ их обработать
  3. Unchecked Exceptions / Runtime Exceptions (extends RuntimeException)

    • NullPointerException, ArrayIndexOutOfBoundsException
    • Не обязательно обрабатывать (но можно)

Try-Catch-Finally блоки

public class ExceptionHandling {
    public static void main(String[] args) {
        try {
            // Код, который может выбросить exception
            int result = 10 / 0;  // ArithmeticException
            
        } catch (ArithmeticException e) {
            // Обработка конкретного exception
            System.out.println("Ошибка: деление на ноль!");
            System.out.println("Message: " + e.getMessage());
            
        } catch (Exception e) {
            // Можно ловить несколько exceptions
            System.out.println("Неизвестная ошибка");
            
        } finally {
            // Выполняется ВСЕГДА (даже если был exception)
            System.out.println("Cleanup операции");
        }
    }
}

Checked vs Unchecked Exceptions

Checked Exception (обязательна обработка)

// Компилятор ТРЕБУЕТ обработку IOException
public void readFile(String path) throws IOException {
    FileInputStream fis = new FileInputStream(path);
    // ❌ ОШИБКА: IOException не обработан!
    // Нужно либо catch, либо throws
}

// ✅ Правильно: с catch
public void readFile(String path) {
    try {
        FileInputStream fis = new FileInputStream(path);
    } catch (IOException e) {
        System.out.println("File not found: " + e.getMessage());
    }
}

// ✅ Правильно: с throws
public void readFile(String path) throws IOException {
    FileInputStream fis = new FileInputStream(path);
    // IOException пойдет выше в call stack
}

Unchecked Exception (опциональна обработка)

// RuntimeException — не нужно обрабатывать
public int divide(int a, int b) {
    return a / b;  // Может выбросить ArithmeticException, но это OK
}

// Если хотим обработать
public int divide(int a, int b) {
    try {
        return a / b;
    } catch (ArithmeticException e) {
        return 0;  // default value
    }
}

Создание Custom Exceptions

// Custom Exception для бизнес-логики
public class InsufficientFundsException extends Exception {
    private double requiredAmount;
    private double availableAmount;
    
    public InsufficientFundsException(double required, double available) {
        super("Insufficient funds: required " + required + ", available " + available);
        this.requiredAmount = required;
        this.availableAmount = available;
    }
    
    public double getShortfall() {
        return requiredAmount - availableAmount;
    }
}

public class BankAccount {
    private double balance;
    
    public void withdraw(double amount) throws InsufficientFundsException {
        if (amount > balance) {
            throw new InsufficientFundsException(amount, balance);
        }
        balance -= amount;
    }
}

// Использование
public static void main(String[] args) {
    BankAccount account = new BankAccount();
    try {
        account.withdraw(1000);  // будет exception
    } catch (InsufficientFundsException e) {
        System.out.println("Error: " + e.getMessage());
        System.out.println("Shortfall: " + e.getShortfall());
    }
}

Exception Chaining (Цепочка исключений)

Когда нужно обернуть одно exception в другое:

public void saveToDatabase(String data) throws DataAccessException {
    try {
        // SQL операция
        jdbcTemplate.update("INSERT INTO ...", data);
        
    } catch (SQLException e) {
        // Оборачиваем SQLException (checked) в DataAccessException (unchecked)
        throw new DataAccessException("Failed to save data", e);
        // cause = e (оригинальная ошибка сохраняется)
    }
}

// Обработка
try {
    saveToDatabase("some data");
} catch (DataAccessException e) {
    System.out.println("Error: " + e.getMessage());
    System.out.println("Cause: " + e.getCause().getMessage());
}

Try-with-resources (автоматическое закрытие ресурсов)

// ❌ Старый способ — нужно закрывать вручную
public void readFile(String path) throws IOException {
    FileInputStream fis = new FileInputStream(path);
    try {
        byte[] data = new byte[1024];
        fis.read(data);
    } finally {
        fis.close();  // не забыть!
    }
}

// ✅ Try-with-resources — автоматически закроет
public void readFile(String path) throws IOException {
    try (FileInputStream fis = new FileInputStream(path)) {
        byte[] data = new byte[1024];
        fis.read(data);
    }
    // fis.close() вызовется автоматически!
}

Multi-catch (Java 7+)

// ❌ Старый способ
try {
    // операции
} catch (FileNotFoundException e) {
    handleError(e);
} catch (SecurityException e) {
    handleError(e);
} catch (IOException e) {
    handleError(e);
}

// ✅ Современный способ
try {
    // операции
} catch (FileNotFoundException | SecurityException | IOException e) {
    handleError(e);
}

Exception Handling Best Practices

1. Ловить конкретные exceptions, не Exception

// ❌ Плохо — ловишь ВСЕ exceptions
try {
    someMethod();
} catch (Exception e) {
    // Могла быть ошибка, которую не ожидаешь
    log.error("Something happened");
}

// ✅ Хорошо — ловишь конкретные
try {
    someMethod();
} catch (IOException e) {
    log.error("File operation failed", e);
} catch (DatabaseException e) {
    log.error("Database error", e);
}

2. Логировать exception с контекстом

// ❌ Плохо
catch (SQLException e) {
    System.out.println("Error");
}

// ✅ Хорошо
catch (SQLException e) {
    log.error("Failed to execute query for user: " + userId, e);
    // e = full stack trace + message
}

3. Не игнорировать exceptions

// ❌ Очень плохо — молча проглотить ошибку
try {
    riskyOperation();
} catch (Exception e) {
    // ничего не делаем
}

// ✅ Если точно нужно игнорировать
try {
    riskyOperation();
} catch (SpecificException e) {
    // это нормально, игнорируем
    log.debug("Expected error: " + e.getMessage());
}

4. Пробрасывать выше, если не можешь обработать

// ❌ Плохо — обрабатываем, но не можем сделать ничего
public void processPayment(Order order) {
    try {
        paymentGateway.charge(order.getTotal());
    } catch (PaymentException e) {
        // что делать? Не знаем
        log.error("Payment failed", e);
    }
}

// ✅ Хорошо — пробрасываем выше
public void processPayment(Order order) throws PaymentException {
    paymentGateway.charge(order.getTotal());
    // Контроллер решит, что делать (refund, retry и т.д.)
}

Exception Handling в REST API

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(InsufficientFundsException.class)
    public ResponseEntity<ErrorResponse> handleInsufficientFunds(
            InsufficientFundsException e) {
        return ResponseEntity
            .status(HttpStatus.BAD_REQUEST)
            .body(new ErrorResponse(
                "INSUFFICIENT_FUNDS",
                e.getMessage()
            ));
    }
    
    @ExceptionHandler(EntityNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleNotFound(
            EntityNotFoundException e) {
        return ResponseEntity
            .status(HttpStatus.NOT_FOUND)
            .body(new ErrorResponse(
                "NOT_FOUND",
                e.getMessage()
            ));
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGenericException(
            Exception e) {
        return ResponseEntity
            .status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body(new ErrorResponse(
                "INTERNAL_ERROR",
                "An unexpected error occurred"
            ));
    }
}

public class ErrorResponse {
    private String code;
    private String message;
    
    public ErrorResponse(String code, String message) {
        this.code = code;
        this.message = message;
    }
}

Проверка Stack Trace

try {
    riskyOperation();
} catch (SQLException e) {
    // Получить полный stack trace
    e.printStackTrace();  // выведет все методы, вызвавшие друг друга
    
    // Или в логи
    log.error("SQL Error", e);
    
    // Получить причину
    Throwable cause = e.getCause();
    
    // Получить все элементы stack
    StackTraceElement[] trace = e.getStackTrace();
}

Производительность Exceptions

// ⚠️ Важно: Exceptions — дорогие!
// Создание exception = создание stack trace = медленно

// ❌ Плохо — exception в цикле
for (String item : items) {
    try {
        parseInteger(item);  // может выбросить exception
    } catch (NumberFormatException e) {
        // если item не число, exception выбросится
    }
}

// ✅ Хорошо — проверка перед операцией
for (String item : items) {
    if (isNumeric(item)) {  // проверяем без exception
        parseInteger(item);
    }
}

// Разница: если много не-чисел, то первый вариант x100 медленнее

Вывод

Java Exceptions — это powerful механизм для обработки ошибок:

  1. Checked vs Unchecked — разные подходы к обработке
  2. Try-catch-finally — базовая структура
  3. Custom exceptions — для business logic
  4. Best practices — ловить конкретные, логировать с контекстом
  5. Performance — exceptions дорогие, используй их для exceptional situations

На собеседовании важно показать, что ты:

  • Понимаешь иерархию exceptions
  • Знаешь разницу между checked и unchecked
  • Умеешь писать custom exceptions
  • Следуешь best practices в обработке ошибок
Что такое Java Exceptions? | PrepBro