Язык программирования Java, как и множество других языков, имеет интегрированные инструменты работы с ошибками — исключительными ситуациями (исключениями), при которых сбой в работе программы обрабатывается специальным кодом, отличным от базового алгоритма.
Благодаря исключениям программист может заранее предвидеть слабые места кодовой базы и предвосхитить возникновение фатальных ошибок в тот момент, когда программа уже выполняется.
Поэтому обработка исключений в Java — хорошая практика, повышающая общую надежность кода.
Цель этой публикации — рассмотреть принципы перехвата и обработки исключений, а также разобрать соответствующие синтаксические конструкции языка, предназначенные для этого.
Все примеры из этого руководства запускались в операционной системе Ubuntu 22.04, установленной на облачном сервере Timeweb Cloud.
В блоге Timeweb Cloud есть отдельная статья, подробно описывающая установку Java в Ubuntu 22.04, но мы также кратко опишем этот процесс ниже.
Установка OpenJDK и запуск приложения
Примеры, показанные в этом руководстве, запускались с помощью OpenJDK. Его установка не представляет особой сложности.
Сперва необходимо обновить список доступных репозиториев:
После этого необходимо запросить список доступных для загрузки версий OpenJDK:
В консоли появится небольшой список:
Как можно заметить, наиболее свежая версия, доступная для загрузки, — openjdk-21-jdk. В этом руководстве используется именно она:
После этого можно проверить корректность установки Java, запросив ее версию:
Консольный вывод будет примерно таким:
Как видно, точная версия OpenJDK — 21.0.5.
Все показанные в этом руководстве примеры необходимо сохранять в отдельном файле с расширением .java:
После чего созданный файл наполняется кодом примера. Например, таким:
Обратите внимание, что имя класса должно совпадать с именем файла.
Далее файл с кодом компилируется:
И запускается:
В консольном терминале появляется соответствующий вывод:
Типы исключений в Java
Все исключения в языке Java имеют определенный тип, ассоциированный с причиной возникновения исключения — конкретным видом сбоя в работе программы.
Всего существует два базовых типа исключений:
-
Checked Exceptions. Такие исключения возникают на этапе компиляции программы. Если их не обработать, программа не скомпилируется.
-
Unchecked Exceptions. Такие исключения возникают на этапе выполнения программы. Если их не обработать, программа прекратит свое выполнение.
Тип Errors можно считать исключением лишь условно — это полноценная ошибка, приводящая к неминуемому прекращению работы программы.
Исключения, которые можно обработать с помощью пользовательского кода и продолжить выполнение программы, — Checked Exceptions и Unchecked Exceptions.
Таким образом, ошибки и исключения в Java являются разными сущностями. Однако и то (Errors), и другое (Checked Exceptions и Unchecked Exceptions), будучи типами, имеет дополнительные подтипы, уточняющие причину сбоя программы.
Облачные серверы
по всему миру с почасовой оплатой.
Checked Exceptions
Рассмотрим пример кода, который вызывает исключение сразу на этапе компиляции программы:
Компиляция прервется, а в консольном терминале появится сообщение об исключении FileNotFoundException:
Если перехватить и обработать это исключение, то код скомпилируется и станет доступен для запуска.
Unchecked Exceptions
Рассмотрим еще один пример кода, который вызывает исключение только на этапе выполнения программы:
Сразу в момент компиляции исключения не возникнут, однако после запуска скомпилированного кода в консольном терминале появится информация об исключении ArrayIndexOutOfBoundsException:
Это означает, что подобное исключение возможно обработать с помощью пользовательского кода, продолжив выполнение программы.
Error
Рассмотрим, наконец, пример кода, который вызывает ошибку во время выполнения программы:
Компиляция пройдет успешно, однако во время выполнения программы в консольном терминале появится сообщение об ошибке StackOverflowError:
В данном случае ошибку никак нельзя обработать — ее можно только исправить.
Классы исключений в Java
Во внутренней реализации языка Java все исключения (и ошибки) представлены в виде набора классов, некоторые из которых наследуют свойства друг друга.
Базовым для всех ошибок и исключений является класс Throwable. От этого класса наследуются два других — Error и Exception, которые являются базовыми для широкого набора других классов, ассоциированных с конкретными типами исключений.
Класс Error описывает исключения типа Error, упомянутых в предыдущем разделе, а класс Exception — типа Checked Exceptions.
В свою очередь, от класса Exception наследуется класс RuntimeException, который описывает исключения типа Unchecked Exceptions.

Сокращенная схема иерархии классов исключений (источник: Java Training School)
В более наглядном виде неполную иерархию классов исключений в Java можно выразить в виде следующего вложенного списка:
-
Throwable
-
Error
-
Exception
-
CloneNotSupportedException
-
InterruptedException
-
ReflectiveOperationException
-
ClassNotFoundException
-
IllegalAccessException
-
InstantiationException
-
NoSuchFieldException
-
NoSuchMethodException
-
RuntimeException
-
NullPointerException
-
ArithmeticException
-
IllegalArgumentException
-
IndexOutOfBoundException
-
NumberFormatException
Соответственно, в каждом классе исключения есть методы для получения дополнительной информации о возникшем сбое.
Полную классификацию исключения Java с учетом дополнительных пакетов можно найти в отдельном справочнике.
Синтаксис работы с исключениями
try и catch
Любые исключения выполняются с помощью специальных блоков try и catch, которые являются стандартными для большинства языков программирования, в том числе и Java.
-
Внутри блока
tryпишется код с потенциальной ошибкой, которая может вызвать исключение.
-
Внутри блока
catchпишется код, который обрабатывает исключение, возникшее в коде ранее указанного блокаtry.
Например, конструкция try-catch может выглядеть так:
Результатом работы этого кода станет следующий консольный вывод:
Конкретно этот пример основан на запрещенной операции деления на ноль, обернутой в блок try, которая вызывает исключения типа ArithmeticException.
Соответственно, в блоке catch выполняется обработка этого исключения, а именно вывод сообщения об ошибке в консольный терминал.
Благодаря такой конструкции программа сможет продолжить выполнение работы после возникновения ошибки во время деления на ноль.
finally
В отличие от многих других языков программирования в Java есть специальный блок finally, относящийся к механизму обработки исключений, который выполняется всегда — вне зависимости от того, было исключение или нет.
Поэтому показанную ранее конструкцию можно дополнить:
После запуска этого кода в консольном терминале появится такой вывод:
Чтобы понять практическую необходимость блока finally, можно рассмотреть следующий пример кода вне какого-либо контекста:
В программе, содержащей такую конструкцию, функция hideLoaderUI() никогда не выполнится, если возникло какое-либо исключение.
В этом случае можно попробовать вызвать функцию hideLoaderUI() в обработчике исключения в случае, если оно возникло, а также после обработчика, если исключения не было:
Однако в этом случае возникает нежелательное дублирование вызовы функции. К тому же, вместо функции может быть полноценный кусок кода, дублирование которого — плохая практика.
Поэтому гарантировать выполнение функции hideLoaderUI() без дублирования ее вызова можно с помощью блока finally:
throw
Язык Java позволяет вручную создавать (выбрасывать) исключения через специальный оператор throw:
Можно даже создать переменную с исключением заранее, а уже потом осуществить ее выброс:
throws
Еще одно ключевое слово throws (обратите внимание на букву «s» в конце) позволяет явно указывать типы исключений (в виде перечисления их классов), которые может выбрасывать объявляемый метод.
Если в таком методе возникнет исключение, оно поднимется наверх в вызывающий код, который должен будет его обработать:
В консольном терминале появится указанное сообщение:
Создание кастомных исключений
Иерархическая структура исключений закономерно разрешает создание пользовательских классов исключений, которые сами по себе наследуются от базовых.
Благодаря пользовательским исключениям язык разрешает реализовывать уникальные для конкретной программы пути обработки сбоев.
Таким образом, к уже стандартным исключениям в Java можно добавить собственные.
Каждое пользовательское исключение, впрочем как и любое предопределенное, возможно обработать через стандартные блоки try-catch-finally:
В консольном терминале появится следующий вывод:
Разворачивайте Java-проекты в облаке
477 ₽/мес
657 ₽/мес
Заключение
В этом руководстве было показано на примерах, зачем нужны исключения в Java, как они возникают (в частности, как выбросить исключение вручную) и как их обработать с помощью соответствующих инструментов языка.
Исключения, доступные для перехвата и обработки, бывают двух типов:
-
Checked Exceptions. Обрабатываются, когда код компилируется.
-
Unchecked Exceptions. Обрабатываются, когда код выполняется.
В дополнение к ним бывают фатальные ошибки, разрешить которые можно только переписывая код:
-
Errors. Обработка невозможна.
При этом существует несколько синтаксических конструкций (в виде блоков) для обработки исключений:
-
try. Код, в котором возможно исключение.
-
catch. Код, который обрабатывает возможное исключение.
-
finally. Код, который выполняется вне зависимости от наличия исключения.
А также ключевые слова для управления процессом выброса исключений:
-
throw. Вручную выбрасывает исключение.
-
throws. Перечисляет возможные исключения внутри объявленного метода.
Полный список методов родительского класса Exception можно посмотреть в официальной документации Oracle.
