Исключения в Java

Это возможность языка для работы с исключительными ситуациями. Сейчас механизм исключений реализован во всех популярных языках. В Python есть такой же механизм, как мы обсудим сейчас, ключевые слова: raise, catch, try.

Примеры исключительных ситуаций: при работе с файлами файл может не открыться (нет прав, нет файла), в процессе чтения доступ к файлу может исчезнуть. При записи файла может закончиться место на диске. При работе с базой данных соединение может оборваться. Или может не установиться соединение из-за отключенного Интернета. Другие примеры, например, при преобразовании строки в число оказывается, что строка не содержит число: Integer.parseInt("42x") — ошибка. Самая частое исключение в Java — это работа со значением null:

String s = null;
System.out.println(s.length());  // Ошибка

Как обрабатывали ошибки раньше, до того, как возник механизм обработки исключений. Псевдокод:

function readTextFromFile() {
    File f = new File("a.txt");
    //если в последнем действии была ошибка (код ошибки не 0)
    if (lastError() != 0) {
        print("При открытии файла возникла ошибка :" + 
               lastErrorMessage());
        return;
    }
    
    // читаем число из файла
    int n = f.readInteger(); 
    if (lastError() != 0) {
        print("При чтении файла возникла ошибка :" + 
               lastErrorMessage());
        f.close(); // TODO, не проверить ли ошибку и после close?
        return;
    }
    
    // читаем до конца строки
    int word = f.readLine();
    if (lastError() != 0) {
        print("При чтении файла возникла ошибка :" + 
               lastErrorMessage());
        f.close(); // TODO, не проверить ли ошибку и после close?
        return;
    }
    
    f.close();
    if (lastError() != 0) {
        print("При закрыти файла возникла ошибка :" + 
               lastErrorMessage());
        return n + word;
    }
    
    return n + word;
}

Что предлагает прогаммирование с механизмом исключений? Предлагается писать код по оптимистичному сценарию:

function readTextFromFile() {
    File f = new File("a.txt");
    int n = f.readInteger(); 
    int word = f.readLine();
    f.close();
    return n + word;
}

Но, если при выполнении одной из операций возникает ошибка, программа останавливается, функция не выполняется далее. Если не смогли прочитать число, то дальше слово уже не читается, файл не закрывается, return не срабатывает. Возникшая ошибка, если она не обработана (как обработать — см. далее), полностью останавливает программу. В Java дополнительно в консоли появляется Stack Trace — информация о том, в какой строке возникла ошибка, какая была цепочка вызовов функций. Т.е. увидим, что из функции main() вызывана функция f(), из нее вызвана функция g(), и в ней в строке 42 возникла такая-то ошибка. (вспомните красный текст в консоли.)

Виды ошибок в Java

Все исключения, которые могут возникать в программе, имеют класс Throwable (то, что можно бросить).

Важная особенность Java, все Exception (кроме RuntimeException) обязательно должны либо обрабатываться, либо нужно явно указывать, что они обрабатываться не будут. Например, если вы читаете файл, вы обязаны либо написать код, который выполняется, если в процессе чтения возникла ошибка. Либо — явно прописать, что вы не собираетесь обрабатывать эту ситуацию.

Пример кода:

Без обработки исключений: ExceptionsExample

Обработка исключения в блоке try-catch: ExceptionsExampleWithTry

Что еще можно делать в try/catch блоках

  1. try (PrintStream ...) — try с ресурсом тоже может иметь catch блок.
  2. catch блоков может быть несколько подряд, они могут ловить разные исключения:
    try {
    } catch (FileNotFoundException e) {
      System.out.println("А файл-то не найден");
    } catch (IOException e) {
      System.out.println("Что-то пошло не так при чтении файла");
    } catch (IllegalArguemntException e) {
      //Хотя, обычно RuntimeException лучше не ловить, лучше исправить код
      System.out.println("...");
    } catch (Exception e) {
      // это сработает при любом исключении, потому что всё это Exception
      // лучше это избегать, обрабатывайте то, что знаете
    }
    
  3. Еще бывает блок finally, он выполняется обязательно, неважно, случилось исключение или нет.