JavaRush/Java блог/Java Developer/Исключения в Java (Java Exception)
Автор
Александр Выпирайленко
Java-разработчик в Toshiba Global Commerce Solutions

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

Статья из группы Java Developer
участников
В повседневной жизни иногда возникают ситуации, которые мы не планировали. Например, встаешь утром на работу, ищешь зарядное устройство к телефону — а его нет. Идешь в ванную, чтобы умыться – отключили воду. Сел в машину – не заводится. Но человек в состоянии довольно легко справиться с такими непредвиденными ситуациями. А как с ними справляются Java-программы, постараемся разобраться в этой статье.

Что такое исключения (exceptions java)

В мире программирования возникновение ошибок и непредвиденных ситуаций при выполнении программы называют исключением (exception). В программе исключения могут возникать в результате неправильных действий пользователя, отсутствии необходимого ресурса на диске, или потери соединения с сервером по сети. Причинами исключений при выполнении программы также могут быть ошибки программирования или неправильное использование API. В отличие от нашего мира, программа должна четко знать, как поступать в такой ситуации. Для этого в Java предусмотрен механизм исключений.

Кратко о ключевых словах try, catch, finally, throws

Обработка исключений в Java основана на использовании в программе следующих ключевых слов:
  • try – определяет блок кода, в котором может произойти исключение;
  • catch – определяет блок кода, в котором происходит обработка исключения;
  • finally – определяет блок кода, который является необязательным, но при его наличии выполняется в любом случае независимо от результатов выполнения блока try.
Эти ключевые слова используются для создания в программном коде специальных обрабатывающих конструкций: try{}catch, try{}catch{}finally, try{}finally{}.
  • throw – используется для возбуждения исключения;
  • throws – используется в сигнатуре методов для предупреждения, о том что метод может выбросить исключение.
Пример использования ключевых слов в Java-программе:
//метод считывает строку с клавиатуры

public String input() throws MyException {//предупреждаем с помощью throws,
// что метод может выбросить исключение MyException
      BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    String s = null;
//в блок try заключаем код, в котором может произойти исключение, в данном
// случае компилятор нам подсказывает, что метод readLine() класса
// BufferedReader может выбросить исключение ввода/вывода
    try {
        s = reader.readLine();
// в блок  catch заключаем код по обработке исключения IOException
    } catch (IOException e) {
        System.out.println(e.getMessage());
// в блоке finally закрываем поток чтения
    } finally {
// при закрытии потока тоже возможно исключение, например, если он не был открыт, поэтому “оборачиваем” код в блок try
        try {
            reader.close();
// пишем обработку исключения при закрытии потока чтения
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

    if (s.equals("")) {
// мы решили, что пустая строка может нарушить в дальнейшем работу нашей программы, например, на результате этого метода нам надо вызывать метод substring(1,2), поэтому мы вынуждены прервать выполнение программы с генерацией своего типа исключения MyException с помощью throw
        throw new MyException("String can not be empty!");
    }
    return s;
}

Для чего нужен механизм исключений?

Посмотрим на пример из реального мира. Представьте, что на автомобильной дороге есть участок с аварийным мостом с ограниченной грузоподъемностью. Если по нему поедет автомобиль с массой, превышающей грузоподъемность моста, он может разрушиться, и ситуация для водителя может стать, мягко говоря, исключительной. Чтобы этого не произошло, дорожная служба заблаговременно устанавливает предупредительные знаки на дороге. Водитель автомобиля, глядя на предупреждающий знак, будет сравнивать массу своего автомобиля с разрешенной для проезда по мосту. Если она превышает ее – он поедет по объездному пути. Благодаря действиям дорожной службы водители грузового транспорта, во-первых, получили возможность заблаговременно изменять свой путь, во-вторых, предупреждены об опасности на основном пути, и, наконец, предупреждены о невозможности использования моста при определенных условиях.
Исключения в Java - 2
Возможность предупреждения и разрешения исключительной ситуации в программе для ее продолжения – одна из причин использования исключений в Java. Механизм исключений также позволяет защитить написанный вами код (программный интерфейс) от неправильного использования пользователем за счет валидации (проверки) входящих данных. Давайте теперь на секунду побудем дорожной службой. Во-первых, вы должны знать места, где автомобилистов могут ждать неприятности. Во-вторых, вам нужно заготовить и установить предупредительные знаки. И, наконец, вам нужно предусмотреть объездные маршруты в случае опасности на основном пути. В Java механизм исключений работает похожим образом. На стадии разработки программы мы «ограждаем» опасные участки кода в отношении исключений с помощью блока try{}, предусматриваем «запасные» пути с помощью блока catch{}, в блоке finally{} мы пишем код, который выполняется в программе при любом исходе. В случаях, когда мы не можем предусмотреть «запасной путь» или намеренно хотим предоставить право его выбора пользователю, мы должны, по крайней мере, предупредить его об опасности. Почему? А вы только вообразите негодование водителя, который доедет до аварийного моста, по которому нельзя проехать, не встретив по дороге ни одного предупреждающего знака! В программировании при написании своих классов и методов мы не всегда можем предвидеть контекст их использования другими разработчиками в своих программах, поэтому не можем предвидеть на 100% правильный путь для разрешения исключительной ситуации. В то же время, правило хорошего тона — предупредить пользователей нашего кода о возможности исключительной ситуации. Механизм исключений Java позволяет нам сделать это с помощью throws – по сути, объявления общего поведения нашего метода, заключающееся в выбрасывании исключения, и предоставляя, таким образом, написание кода по обработке исключения в Java пользователю метода.

Предупреждение о «неприятностях»

Когда вы не планируете обрабатывать исключение в своем методе, но хотите предупредить пользователей метода о возможных исключительных ситуациях — используйте ключевое слово throws. Это ключевое слово в сигнатуре метода означает, что при определенных условиях метод, может выбросить исключение. Такое предупреждение является частью интерфейса метода и предоставляет право пользователю на собственный вариант реализации обработчика исключения. После throws мы указываем тип выбрасываемого исключения. Обычно это наследники класса Exception Java. Поскольку Java является объектно-ориентированным языком, все исключения в Java представляют собой объекты.
Исключения в Java - 3

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

При возникновении ошибки в процессе выполнения программы исполняющая среда JVM создает объект нужного типа из иерархии исключений Java – множества возможных исключительных ситуаций, унаследованных от общего «предка» – класса Throwable. Исключительные ситуации, возникающие в программе, можно разделить на две группы:
  1. Ситуации, при которых восстановление дальнейшей нормальной работы программы невозможно
  2. Восстановление возможно.
К первой группе относят ситуации, когда возникают исключения, унаследованные из класса Error. Это ошибки, возникающие при выполнении программы в результате сбоя работы JVM, переполнения памяти или сбоя системы. Обычно они свидетельствуют о серьезных проблемах, устранить которые программными средствами невозможно. Такой вид исключений в Java относится к неконтролируемым (unchecked) на стадии компиляции. К этой группе также относят RuntimeException – исключения, наследники класса Exception, генерируемые JVM во время выполнения программы. Часто причиной возникновения их являются ошибки программирования. Эти исключения также являются неконтролируемыми (unchecked) на стадии компиляции, поэтому написание кода по их обработке не является обязательным. Ко второй группе относят исключительные ситуации, предвидимые еще на стадии написания программы, и для которых должен быть написан код обработки. Такие исключения являются контролируемыми (checked). Основная часть работы разработчика на Java при работе с исключениями – обработка таких ситуаций.

Создание исключения

При исполнении программы исключение генерируется JVM или вручную, с помощью оператора throw. При этом в памяти создается объект исключения и выполнение основного кода программы прерывается, а обработчик исключений JVM пытается найти способ обработать исключение.

Обработка исключения

Создание блоков кода, для которых мы предусматриваем обработку исключений в Java, производится в программе с помощью конструкций try{}catch, try{}catch{}finally, try{}finally{}.
Исключения в Java - 4
При возбуждении исключения в блоке try обработчик исключения ищется в следующем за ним блоке catch. Если в catch есть обработчик данного типа исключения – управление переходит к нему. Если нет, то JVM ищет обработчик этого типа исключения в цепочке вызовов методов до тех пор, пока не будет найден подходящий catch. После выполнения блока catch управление передается в необязательный блок finally. В случае, если подходящий блок catch не найден, JVM останавливает выполнение программы, и выводит стек вызовов методов – stack trace, выполнив перед этим код блока finally при его наличии. Пример обработки исключений:
public class Print {

     void print(String s) {
        if (s == null) {
            throw new NullPointerException("Exception: s is null!");
        }
        System.out.println("Inside method print: " + s);
    }

    public static void main(String[] args) {
        Print print = new Print();
        List list= Arrays.asList("first step", null, "second step");

        for (String s:list) {
            try {
                print.print(s);
            }
            catch (NullPointerException e) {
                System.out.println(e.getMessage());
                System.out.println("Exception was processed. Program continues");
            }
            finally {
                System.out.println("Inside bloсk finally");
            }
            System.out.println("Go program....");
            System.out.println("-----------------");
        }

    }
    }
Результаты работы метода main:
Inside method print: first step
Inside bloсk finally
Go program....
-----------------
Exception: s is null!
Exception was processed. Program continues
Inside bloсk finally
Go program....
-----------------
Inside method print: second step
Inside bloсk finally
Go program....
-----------------
Блок finally обычно используется для того, чтобы закрыть открытые в блоке try потоки или освободить ресурсы. Однако при написании программы не всегда возможно уследить за закрытием всех ресурсов. Для облегчения нашей жизни разработчики Java предложили нам конструкцию try-with-resources, которая автоматически закрывает ресурсы, открытые в блоке try. Наш первый пример можно переписать так с помощью try-with-resources:
public String input() throws MyException {
    String s = null;
    try(BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))){
        s = reader.readLine();
   } catch (IOException e) {
       System.out.println(e.getMessage());
   }
    if (s.equals("")){
        throw new MyException ("String can not be empty!");
    }
    return s;
}
Благодаря возможностям Java, начиная с версии 7, мы также можем объединять перехват разнотипных исключений в одном блоке, делая код более компактным и читабельным. Например:
public String input() {
    String s = null;
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
        s = reader.readLine();
        if (s.equals("")) {
            throw new MyException("String can not be empty!");
        }
    } catch (IOException | MyException e) {
        System.out.println(e.getMessage());
    }
    return s;
}

Итоги

Использование исключений в Java позволяет повысить отказоустойчивость программы за счет использования «запасных» путей, отделить логику основного кода от кода обработки исключительных ситуаций за счет использования блоков catch, а также дает нам возможность переложить обработку исключений на пользователя нашего кода с помощью throws.
Комментарии (58)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Элина
Уровень 11
29 мая 2023, 13:08
правильно ли понимаю, что когда я работаю с проектом, в котором есть несколько потоков исполнения, может быть вот такая ситуация. Один из этих потоков запускается и завершается успешно, а затем выбрасывает исключение внутри блока try-catch. Оставшиеся потоки исполнения продолжают свою работу, но никакой код в блоке finally не выполняется. Тогда блок finally при обработке исключений не будет выполнен?
Роман И Backend Developer в СберТех
19 июля 2023, 12:12
всегда выполняется для конкретного потока. Остальные то тут причём? есть только случай это смерть JVM, выход exit() сразу ... 2 или три случая, все они не касаются нормальной работы
Иван
Уровень 23
29 мая 2023, 11:07
Отличная статья! Большое Вам спасибо за разъяснение!
Tural Backend Developer
16 мая 2023, 12:16
Отлавливают ли Error ? в OCS пишется что нельзя их отлавливать
Justinian Judge в Mega City One Master
21 мая 2023, 07:58
Слово "нельзя" здесь не совсем удачное, потому что имеет два разных толкования "физически нельзя отловить", "нельзя отлавливать, как осуждение или убедительный совет (пример - "нельзя же пальцы в розетку тыкать - то есть физически возможно, но это осуждается)" Вообще вопрос простой , на собесе любят задавать, у того кто знает иерархию наследования и может ответить на вопросы может ли Throwable быть в catch блоке, естественно могут быть и наследники, в том числе и Error. Синтаксически в коде прописать отлов Error конечно можно, это легко проверяется в IDE Следующий вопрос, есть ли смысл. В большинстве случаев конечно же смысла мало, поскольку главное отличие Error от Exception в том, что Error это критические ошибки, после которых JVM как правило уже не может оправиться, и соответственно обрабатывать попросту нечем. Но есть такие Error после которых система вполне может восстановиться, поэтому простой ответ "Error физически можно поместить в catch блок, но это не имеет смысла как правило, поскольку Error критические ошибки прекращающие функционирование программы, которая уже не может восстановиться". В уме держим вторую часть, что если специфика Error такова, что после нее программа вполне может продолжить работу, то если нужно то можно и отлавливать такое при сильной необходимости, но это достаточно редкие кейсы.
Dok3R73
Уровень 42
26 марта 2023, 06:09
Спасибо за труды, очень доступно написано👍
Art Sch
Уровень 13
11 января 2023, 12:06
я читаю про исключения на 1м и в принципе понимаю, но не очень.... ps: зачем только я начал с java core... pss: если вы это читаете, и я до сих пор на первом, то либо я прохожу другой курс, либо читаю книгу по джаве, параллельно проходя этот курс, либо решил взять перерыв на неопределенный срок времени. никогда не сдамся)
Zangar Musa
Уровень 11
2 марта 2023, 23:09
не сдавайся
Mari_Pi
Уровень 11
14 декабря 2023, 14:39
Как успехи...?
Art Sch
Уровень 13
16 февраля, 19:16
Ну такое, если честно, понял, что это скорее не мое. Углубился в предпринимательскую деятельность, начал дело, которое по душе) В любом случае это большой опыт решения задач, развития логики. Мне было очень интересно читать данный курс и решать задачки, засиживаться на одном задании по 2+ дня, а то и больше, смотреть CS50 8) Очень благодарен джаве и джаварашу за то, что дали понять, что важно не останавливаться на пути и просто делать (даже с ошибками), хоть я и остановился, считаю, что это не потому, что у меня не получалось и я забил, а тк это занятие не сильно приносило мне удовольствие и я решил, что заниматься нелюбимым делом не для меня, начал искать себя и в итоге нашел))
6 ноября 2021, 16:01
Есть подозрение, что так будет правильнее.
Kango Vince
Уровень 6
Expert
19 ноября 2021, 15:07
Так это и имеется ввиду. Именно это же и имеется ввиду на дорожном знаке.
27 ноября 2021, 06:50
Нет, если знак убрать, вы не поедете через finally.
Alexey Korshikov
Уровень 11
19 апреля 2022, 20:15
саtch - на основной дороге перед знаком "проезд закрыт". finally - тоже на основной, после знака. А вот между catch и finally - куча вариантов объезда, именованные типом исключения.
22 апреля 2022, 13:20
куча вариантов объезда, у каждого из них свой кетч
Mark Vladimirovich
Уровень 16
25 марта 2021, 10:33
обращу внимание на некоторую неточность. цитата "Создание исключения При исполнении программы исключение генерируется JVM или вручную, с помощью оператора throw" в java исключения это тоже объекты поэтому создается исключение так же как объект new Exception. а бросается в программе с помощью оператора throw. обычно эти операции объединяют в одну throw new Exception("aaa");
Dias
Уровень 4
19 сентября 2020, 18:18
если что я пишу это с 3 уровня. Под конец лекций я читал статью про бафридер, после нашел там ссылку на потоки вводов, а потом чтобы понять что там говориться ввел гугл про исключение и нашел эту статью, спасибо автору, это статья очень помогла. PS если ты читаешь этот комментарий и видишь что у меня нет прогресса(то есть если я все еще на 3 уровне или чуточку больше), то скажи мне, что я нуб и не дошел до 40 лвла
Rim Kayumov
Уровень 30
22 сентября 2020, 11:02
ну ты и нуб
Alukard Vampire hunter в The Hellsing Expert
28 сентября 2020, 20:55
нубяра)
Marina Konoplya
Уровень 13
10 октября 2020, 18:20
Почему до сих пор не на 40 ?)
PurpleDie
Уровень 8
12 октября 2020, 19:27
ну ты и нубло
Саша Приходько
Уровень 7
12 ноября 2020, 20:43
Ну, получается, нуб)
Dias
Уровень 4
16 ноября 2020, 15:02
я реально нуб!! сдался после 4 уровня!!! фууууу
2 декабря 2020, 21:26
"ты нуб и не дошел до 40 лвла" Ну чего ты сдался? Все не так страшно .
Имя Фамилия
Уровень 19
16 декабря 2020, 14:05
Напуркуа бы на 3-м уровне читать лекции про исключения.. На момент коммента от нубяры (19 сент) я тоже был на 3-м, но я пока не сдаюсь, я грызу.. Сдаваться не наш метод )) Хотя заниматься учёбой после работы и на выходных - то ещё удовольствие..
Дима
Уровень 1
12 ноября 2021, 22:46
когда 40 левл ?)
Gennagiy
Уровень 2
23 марта 2022, 08:20
Мужик, Надеюсь ты прокачался в чем-то другом.
misha_lazarev
Уровень 34
29 апреля 2022, 11:43
Держись еще не много осталось🤨
Джуня
Уровень 21
13 июля 2022, 19:16
Ты нуб
Almir M
Уровень 1
26 августа 2022, 13:41
Ты до сих пор только на 4 уровне!!!!
Andriano CHANNEL Повар в Кафе "ДУША"
20 декабря 2022, 16:43
Нуб, отвратительно!
Ильдар
Уровень 2
29 ноября 2023, 06:35
Мужик, ну ты чего?
Артём
Уровень 16
15 июля 2020, 14:58
Когда живёшь в Кубинке -_- (отсылка к картинке с объездом)
5 мая 2020, 15:33
Непонятно как реализуется конструкция try-with-resources и ее особенности?
Tatyana
Уровень 22
3 января 2022, 06:18
Все в последнем примере, после кл.чевого слова try ставятся круглые скобки, в них указывается открытие потока или соединения, собственно и все))))