Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Проверяемые и непроверяемые исключения

В Java исключения делятся на два вида. Проверяемые (checked) компилятор заставляет обработать заранее, иначе программа не скомпилируется. Непроверяемые (unchecked) можно не обрабатывать — компилятор о них молчит. Это особенность именно Java: в большинстве других языков такого деления нет.

Какие исключения какие

Правило простое:

  • Непроверяемые — RuntimeException, все его наследники и ещё Error. Обычно это ошибки в логике программы: NullPointerException, ArithmeticException, IllegalArgumentException.
  • Проверяемые — все остальные наследники Exception. Это ситуации, которые от программиста не зависят: файла нет, сеть недоступна. Самый известный пример — IOException, ошибка ввода-вывода.

Метод Files.readString читает файл и может выбросить IOException. Поэтому без обработки компилятор его вызвать не даст:

void main() {
    String text = Files.readString(Path.of("notes.txt"));
    IO.println(text);
}

Результат:

Main.java:2: error: unreported exception IOException; must be caught or declared to be thrown
    String text = Files.readString(Path.of("notes.txt"));
                                  ^
1 error
error: compilation failed

Вариант 1: перехватить

Первый способ выполнить требование компилятора — обернуть вызов в try-catch:

void main() {
    try {
        String text = Files.readString(Path.of("notes.txt"));
        IO.println(text);
    } catch (IOException e) {
        IO.println("Не удалось прочитать файл: " + e.getMessage());
    }
}

Результат:

Не удалось прочитать файл: notes.txt

Файла notes.txt нет, поэтому сработал catch. Сообщение IOException здесь — просто имя файла.

Вариант 2: объявить throws

Второй способ — честно сказать в объявлении метода, что он может выбросить исключение. Для этого служит слово throws. Тогда обрабатывать ошибку будет тот, кто вызвал метод:

String readNotes() throws IOException {
    return Files.readString(Path.of("notes.txt"));
}
 
void main() {
    try {
        IO.println(readNotes());
    } catch (NoSuchFileException e) {
        IO.println("Файла нет: " + e.getMessage());
    } catch (IOException e) {
        IO.println("Другая ошибка ввода-вывода");
    }
}

Результат:

Файла нет: notes.txt

NoSuchFileException — наследник IOException, поэтому его можно перехватить отдельно. Слово throws можно написать и у main — тогда при ошибке программа просто завершится с трассировкой стека.

Непроверяемые исключения

Для RuntimeException и его наследников компилятор ничего не требует. Метод может выбросить их без всяких throws:

int percent(int part, int total) {
    if (total <= 0) {
        throw new IllegalArgumentException("total должен быть больше нуля");
    }
    return part * 100 / total;
}
 
void main() {
    IO.println(percent(3, 4) + "%");
}

Результат:

75%

Непроверяемые исключения обычно не перехватывают, а исправляют код, чтобы они не возникали: проверяют null, границы массива, входные данные.

Обернуть в непроверяемое

Иногда проверяемое исключение нельзя пробросить дальше — например, внутри лямбды, которой throws не положен. Тогда его заворачивают в непроверяемое. Для IOException есть готовый класс UncheckedIOException:

void main() {
    List<String> names = List.of("a.txt", "b.txt");
 
    try {
        names.forEach(name -> {
            try {
                IO.println(Files.readString(Path.of(name)));
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        });
    } catch (UncheckedIOException e) {
        IO.println("Ошибка: " + e.getCause().getClass().getSimpleName());
    }
}

Результат:

Ошибка: NoSuchFileException

Исходное исключение сохраняется как причина (cause) и доступно через getCause().

Частая ошибка

Пустой catch, написанный лишь для того, чтобы «замолчал» компилятор, прячет ошибку:

void main() {
    String text = "";
    try {
        text = Files.readString(Path.of("config.txt"));
    } catch (IOException e) {
    }
    IO.println("Настройки: [" + text + "]");
}

Результат:

Настройки: []

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

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.