RU | EN | DE

Что такое исключение

Исключение (Exception) — это событие, которое прерывает нормальный ход выполнения программы.
Когда возникает ошибка, Java создаёт объект класса Throwable и передаёт его в runtime-среду (JVM).
Если исключение не перехвачено (try-catch), программа завершается с ошибкой.

Иерархия Throwable

java.lang.Object
   └── java.lang.Throwable
        ├── Exception
        │     ├── RuntimeException
        │     └── ... (другие checked исключения)
        └── Error

📗 Объяснение ветвей:

КлассНазначение
ThrowableБазовый класс для всех ошибок и исключений
ErrorОшибки JVM (не нужно обрабатывать вручную) — например, OutOfMemoryError, StackOverflowError
ExceptionИсключения, которые можно (и нужно) обрабатывать
RuntimeExceptionИсключения времени выполнения (unchecked) — NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException и др.

Checked vs Unchecked Exceptions

ТипПримерКогда возникаетНужно ли объявлять в throwsОбрабатывается?
CheckedIOException, SQLException, FileNotFoundExceptionОшибки, предсказуемые на этапе компиляции (например, при работе с файлами, сетью)✅ ДаОбязательно (try-catch или throws)
Unchecked (Runtime)NullPointerException, IndexOutOfBoundsException, ArithmeticExceptionОшибки, возникающие во время выполнения (обычно из-за багов)❌ НетМожно обрабатывать, но не требуется

📙 Пример: Checked Exception

import java.io.FileReader;
import java.io.IOException;
 
public class CheckedExample {
    public static void main(String[] args) {
        try {
            FileReader reader = new FileReader("data.txt");
            reader.read();
            reader.close();
        } catch (IOException e) {
            System.out.println("Ошибка при чтении файла: " + e.getMessage());
        }
    }
}

💡 Без блока try-catch компилятор не даст запустить код, потому что FileReader выбрасывает IOException.

📙 Пример: Unchecked Exception

public class UncheckedExample {
    public static void main(String[] args) {
        int[] nums = {1, 2, 3};
        System.out.println(nums[5]); // ArrayIndexOutOfBoundsException
    }
}

💡 Можно скомпилировать, но при запуске получим ошибку во время выполнения.

Блоки try, catch, finally

✅ Основной синтаксис

try {
    // код, который может выбросить исключение
} catch (ExceptionType e) {
    // обработка исключения
} finally {
    // выполняется всегда, независимо от наличия исключения
}

📙 Пример:

try {
    int result = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("Деление на ноль!");
} finally {
    System.out.println("Завершение операции");
}
Вывод:
Деление на ноль!
Завершение операции

💡 finally выполняется всегда, даже если в catch есть return.

try-with-resources (Java 7+)

Назначение:
Автоматически закрывает ресурсы (например, файлы, потоки, соединения), реализующие интерфейс AutoCloseable.

Синтаксис:

try (FileReader reader = new FileReader("data.txt")) {
    int c = reader.read();
} catch (IOException e) {
    e.printStackTrace();
}

💡 После выхода из try метод reader.close() вызывается автоматически, даже при исключении. 📍 Работает с любым объектом, реализующим AutoCloseable или Closeable.

Пример с JDBC:

try (Connection conn = DriverManager.getConnection(url);
     PreparedStatement stmt = conn.prepareStatement(sql)) {
 
    stmt.executeUpdate();
} catch (SQLException e) {
    e.printStackTrace();
}

Ключевые слова throw и throws

throw — выбрасывает исключение вручную

if (age < 0) {
    throw new IllegalArgumentException("Возраст не может быть отрицательным!");
}

💡Используется для генерации ошибок в коде или валидации данных.

throws — объявляет, что метод может выбросить исключение

void readFile(String path) throws IOException {
    FileReader reader = new FileReader(path);
}

💡Объявление throws обязательно для checked exceptions, если они не перехватываются внутри метода.

Множественные catch блоки

try {
    FileReader reader = new FileReader("file.txt");
    int x = 10 / 0;
} catch (FileNotFoundException e) {
    System.out.println("Файл не найден");
} catch (ArithmeticException e) {
    System.out.println("Ошибка деления");
} catch (Exception e) {
    System.out.println("Общая ошибка");
}

📍Блоки catch проверяются сверху вниз, от более конкретных к более общим.

Множественный catch (Java 7+)

Если нужно обработать несколько исключений одинаковым образом, можно объединить их:

try {
    // ...
} catch (IOException | SQLException e) {
    e.printStackTrace();
}

Создание собственных исключений

Можно создать собственное исключение, унаследовавшись от Exception или RuntimeException.

Checked custom exception:

class MyCheckedException extends Exception {
    public MyCheckedException(String message) {
        super(message);
    }
}

Unchecked custom exception:

class MyRuntimeException extends RuntimeException {
    public MyRuntimeException(String message) {
        super(message);
    }
}

📍Разница:

  • Наследуешь Exception → нужно объявлять throws.
  • Наследуешь RuntimeException → можно не объявлять.

Типичные ошибки

❌ Проглатывание исключений:

try {
    doSomething();
} catch (Exception e) {
    // пусто — плохо!
}

✅ Лучше:

try {
    doSomething();
} catch (Exception e) {
    log.error("Ошибка при выполнении", e);
    throw e; // или обернуть в собственное исключение
}

❌ Неверный порядок catch:

try { ... }
catch (Exception e) { ... }
catch (IOException e) { ... } // недостижимый код

✅ Правильно — от частного к общему.

⚙️Best Practices

  • ✅ Используй try-with-resources для любых IO/JDBC операций.
  • ✅ Лови только те исключения, которые можешь обработать осмысленно.
  • ✅ Не используй Exception или Throwable в catch без причины.
  • ✅ Создавай собственные исключения для бизнес-логики.
  • ✅ Не используй исключения для обычного контроля потока.
  • ✅ Всегда логируй ошибки (Logger, SLF4J, Logback и т. д.).