Перехват и обработка исключений в Java: инструкция

    Команда Simple-Server
    03.06.2026
    12 мин

    Материал подготовлен командой Simple-Server для администраторов VPS и выделенных серверов. Команды и пути проверяйте на тестовой машине перед production.

    Установка OpenJDK и запуск приложения

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

    Сперва необходимо обновить список доступных репозиториев:

    После этого необходимо запросить список доступных для загрузки версий OpenJDK:

    sudo apt search openjdk | grep -E 'openjdk-.*-jdk/'

    В консоли появится небольшой список:

    WARNING: apt does not have a stable CLI interface. Use with caution in scripts. openjdk-11-jdk/jammy-updates,jammy-security 11.0.25+9-1ubuntu1~22.04 amd64 openjdk-17-jdk/jammy-updates,jammy-security 17.0.13+11-2ubuntu1~22.04 amd64 openjdk-18-jdk/jammy-updates,jammy-security 18.0.2+9-2~22.04 amd64 openjdk-19-jdk/jammy-updates,jammy-security 19.0.2+7-0ubuntu3~22.04 amd64 openjdk-21-jdk/jammy-updates,jammy-security,now 21.0.5+11-1ubuntu1~22.04 amd64 [installed] openjdk-8-jdk/jammy-updates,jammy-security 8u432-ga~us1-0ubuntu2~22.04 amd64

    Как можно заметить, наиболее свежая версия, доступная для загрузки, — openjdk-21-jdk. В этом руководстве используется именно она:

    sudo apt install openjdk-21-jdk

    После этого можно проверить корректность установки Java, запросив ее версию:

    Консольный вывод будет примерно таким:

    openjdk 21.0.5 2024-10-15 OpenJDK Runtime Environment (build 21.0.5+11-Ubuntu-1ubuntu122.04) OpenJDK 64-Bit Server VM (build 21.0.5+11-Ubuntu-1ubuntu122.04, mixed mode, sharing)

    Как видно, точная версия OpenJDK — 21.0.5.

    Все показанные в этом руководстве примеры необходимо сохранять в отдельном файле с расширением .java:

    После чего созданный файл наполняется кодом примера. Например, таким:

    class App { public static void main(String[] args) { System.out.println("Этот текст выводится в консоль"); } }

    Обратите внимание, что имя класса должно совпадать с именем файла.

    Далее файл с кодом компилируется:

    В консольном терминале появляется соответствующий вывод:

    Этот текст выводится в консоль

    Все исключения в языке Java имеют определенный тип, ассоциированный с причиной возникновения исключения — конкретным видом сбоя в работе программы.

    Всего существует два базовых типа исключений:

    • Checked Exceptions. Такие исключения возникают на этапе компиляции программы. Если их не обработать, программа не скомпилируется.

    Checked Exceptions. Такие исключения возникают на этапе компиляции программы. Если их не обработать, программа не скомпилируется.

    • Unchecked Exceptions. Такие исключения возникают на этапе выполнения программы. Если их не обработать, программа прекратит свое выполнение.

    Unchecked Exceptions. Такие исключения возникают на этапе выполнения программы. Если их не обработать, программа прекратит свое выполнение.

    Тип Errors можно считать исключением лишь условно — это полноценная ошибка, приводящая к неминуемому прекращению работы программы.

    Исключения, которые можно обработать с помощью пользовательского кода и продолжить выполнение программы, — Checked Exceptions и Unchecked Exceptions.

    Таким образом, ошибки и исключения в Java являются разными сущностями. Однако и то (Errors), и другое (Checked Exceptions и Unchecked Exceptions), будучи типами, имеет дополнительные подтипы, уточняющие причину сбоя программы.

    Рассмотрим пример кода, который вызывает исключение сразу на этапе компиляции программы:

    import java.io.File; import java.util.Scanner; public class App { public static void main(String[] args) { File someFile = new File("someFile.txt"); // создание указателя на файл Scanner scanner = new Scanner(someFile); // парсинг содержимого файла } }

    Компиляция прервется, а в консольном терминале появится сообщение об исключении FileNotFoundException:

    App.java:7: error: unreported exception FileNotFoundException; must be caught or declared to be thrown Scanner scanner = new Scanner(someFile); ^ 1 error

    Если перехватить и обработать это исключение, то код скомпилируется и станет доступен для запуска.

    Рассмотрим еще один пример кода, который вызывает исключение только на этапе выполнения программы:

    class App { public static void main(String[] args) { int[] someArray = {1, 2, 3, 4, 5}; // создание массива из 5 элементов System.out.println(someArray[10]); // попытка доступа к несуществующему элементу } }

    Сразу в момент компиляции исключения не возникнут, однако после запуска скомпилированного кода в консольном терминале появится информация об исключении ArrayIndexOutOfBoundsException:

    Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 5 at app.main(app.java:4)

    Это означает, что подобное исключение возможно обработать с помощью пользовательского кода, продолжив выполнение программы.

    Рассмотрим, наконец, пример кода, который вызывает ошибку во время выполнения программы:

    public class App { static int i = 0; public static int showSomething(int x) { i = i + 2; return i + showSomething(i + 2); } public static void main(String[] args) { App.showSomething(i); // вызываем переполнение стека } }

    Компиляция пройдет успешно, однако во время выполнения программы в консольном терминале появится сообщение об ошибке StackOverflowError:

    Exception in thread "main" java.lang.StackOverflowError at java.base/java.io.BufferedOutputStream.implWrite(BufferedOutputStream.java:220) at java.base/java.io.BufferedOutputStream.write(BufferedOutputStream.java:200) at java.base/java.io.PrintStream.implWrite(PrintStream.java:643)

    В данном случае ошибку никак нельзя обработать — ее можно только исправить.

    Во внутренней реализации языка Java все исключения (и ошибки) представлены в виде набора классов, некоторые из которых наследуют свойства друг друга.

    Базовым для всех ошибок и исключений является класс Throwable. От этого класса наследуются два других — Error и Exception , которые являются базовыми для широкого набора других классов, ассоциированных с конкретными типами исключений.

    Класс Error описывает исключения типа Error , упомянутых в предыдущем разделе, а класс Exception — типа Checked Exceptions.

    В свою очередь, от класса Exception наследуется класс RuntimeException , который описывает исключения типа Unchecked Exceptions.

    В более наглядном виде неполную иерархию классов исключений в Java можно выразить в виде следующего вложенного списка:

    • Throwable
    * **Error** * **Exception** * CloneNotSupportedException * InterruptedException * Reflective­Operation­Exception * ClassNotFoundException * Illegal­Access­Exception * Instantiation­Exception * No­Such­Field­Exception * No­Such­Method­Exception * **RuntimeException** * NullPointerException * ArithmeticException * IllegalArgumentException * IndexOutOfBoundException * NumberFormatException
    * CloneNotSupportedException * InterruptedException * Reflective­Operation­Exception * ClassNotFoundException * Illegal­Access­Exception * Instantiation­Exception * No­Such­Field­Exception * No­Such­Method­Exception * **RuntimeException** * NullPointerException * ArithmeticException * IllegalArgumentException * IndexOutOfBoundException * NumberFormatException
    • CloneNotSupportedException

    • InterruptedException

    • Reflective­Operation­Exception

    * ClassNotFoundException * Illegal­Access­Exception * Instantiation­Exception * No­Such­Field­Exception * No­Such­Method­Exception
    • RuntimeException
    * NullPointerException * ArithmeticException * IllegalArgumentException * IndexOutOfBoundException * NumberFormatException

    CloneNotSupportedException

    Reflective­Operation­Exception

    • ClassNotFoundException

    • Illegal­Access­Exception

    • Instantiation­Exception

    • No­Such­Field­Exception

    • No­Such­Method­Exception

    • NullPointerException

    • ArithmeticException

    • IllegalArgumentException

    • IndexOutOfBoundException

    • NumberFormatException

    Соответственно, в каждом классе исключения есть методы для получения дополнительной информации о возникшем сбое.

    Полную классификацию исключения Java с учетом дополнительных пакетов можно найти в отдельном справочнике.

    Синтаксис работы с исключениями

    Любые исключения выполняются с помощью специальных блоков try и catch, которые являются стандартными для большинства языков программирования, в том числе и Java.

    • Внутри блока try пишется код с потенциальной ошибкой, которая может вызвать исключение.

    Внутри блока try пишется код с потенциальной ошибкой, которая может вызвать исключение.

    • Внутри блока catch пишется код, который обрабатывает исключение, возникшее в коде ранее указанного блока try.

    Внутри блока catch пишется код, который обрабатывает исключение, возникшее в коде ранее указанного блока try.

    Например, конструкция try-catch может выглядеть так:

    public class App { public static void main(String[] args) { try { // тут пишется код, способный вызвать исключение int someVariable = 5 / 0; System.out.println("А кто сказал, что на ноль делить нельзя?"); } catch (ArithmeticException someExeption) { // тут пишется код, способный обработать исключение System.out.println("Вообще-то на ноль делить нельзя..."); } } }

    Результатом работы этого кода станет следующий консольный вывод:

    Вообще-то на ноль делить нельзя...

    Конкретно этот пример основан на запрещенной операции деления на ноль, обернутой в блок try, которая вызывает исключения типа ArithmeticException.

    Соответственно, в блоке catch выполняется обработка этого исключения, а именно вывод сообщения об ошибке в консольный терминал.

    Благодаря такой конструкции программа сможет продолжить выполнение работы после возникновения ошибки во время деления на ноль.**
    **

    В отличие от многих других языков программирования в Java есть специальный блок finally, относящийся к механизму обработки исключений, который выполняется всегда — вне зависимости от того, было исключение или нет.

    Поэтому показанную ранее конструкцию можно дополнить:

    public class App { public static void main(String[] args) { try { // тут пишется код, способный вызвать исключение int someVariable = 5 / 0; } catch (ArithmeticException someExeption) { // тут пишется код, который обрабатывать исключение System.out.println("Вообще-то на ноль делить нельзя..."); } finally { // тут пишется код, которые выполняется всегда System.out.println("Какая разница, делится ли число на ноль или нет? Этот текст все равно появится!"); } } }

    После запуска этого кода в консольном терминале появится такой вывод:

    Вообще-то на ноль делить нельзя... Какая разница, делится ли число на ноль или нет? Этот текст все равно появится!

    Чтобы понять практическую необходимость блока finally, можно рассмотреть следующий пример кода вне какого-либо контекста:

    try { parseJson(response.json); } catch (JSONException someExeption) { System.out.println("Похоже с JSON что-то не так..."); } // некая функция, скрывающая индикатор загрузки hideLoaderUI();

    В программе, содержащей такую конструкцию, функция hideLoaderUI() никогда не выполнится, если возникло какое-либо исключение.

    В этом случае можно попробовать вызвать функцию hideLoaderUI() в обработчике исключения в случае, если оно возникло, а также после обработчика, если исключения не было:

    try { parseJson(response.json); } catch (JSONException someExeption) { hideLoaderUI(); // дубль System.out.println("Похоже с JSON что-то не так..."); } hideLoaderUI(); // дубль

    Однако в этом случае возникает нежелательное дублирование вызовы функции. К тому же, вместо функции может быть полноценный кусок кода, дублирование которого — плохая практика.

    Поэтому гарантировать выполнение функции hideLoaderUI() без дублирования ее вызова можно с помощью блока finally:

    try { parseJson(response.json); } catch (JSONException someExeption) { System.out.println("Похоже с JSON что-то не так..."); } finally { // индикатор загрузки скроется в любом случае hideLoaderUI(); }

    Язык Java позволяет вручную создавать (выбрасывать) исключения через специальный оператор throw:

    public class App { public static void main(String[] args) { throw new Exception("Кажется случилось что-то странное..."); } }

    Можно даже создать переменную с исключением заранее, а уже потом осуществить ее выброс:

    public class App { public static void main(String[] args) { var someException = new Exception("Кажется случилось что-то странное..."); throw someException; } }

    Еще одно ключевое слово throws (обратите внимание на букву «s» в конце) позволяет явно указывать типы исключений (в виде перечисления их классов), которые может выбрасывать объявляемый метод.

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

    public class App { public static void someMethod() throws ArithmeticException, NullPointerException, InterruptedException { int someVariable = 5 / 0; } public static void main(String[] args) { try { App.someMethod(); } catch (Exception someExeption) { System.out.println("Опять делим на ноль? А ты знаешь, что такое безумие?"); } } }

    В консольном терминале появится указанное сообщение:

    Опять делим на ноль? А ты знаешь, что такое безумие?

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

    Иерархическая структура исключений закономерно разрешает создание пользовательских классов исключений, которые сами по себе наследуются от базовых.

    Благодаря пользовательским исключениям язык разрешает реализовывать уникальные для конкретной программы пути обработки сбоев.

    Таким образом, к уже стандартным исключениям в Java можно добавить собственные.

    Каждое пользовательское исключение, впрочем как и любое предопределенное, возможно обработать через стандартные блоки try-catch-finally:

    class MyOwnException extends Exception { public MyOwnException(String message) { super(message); // запуск конструктора родительского класса System.out.println("Внимание! Сейчас вывалится исключение!"); } } public class App { public static void main(String[] args) { try { throw new MyOwnException("Просто исключение. Без объяснения причин. Кто-то против?"); } catch (MyOwnException someException) { System.out.println(someException.getMessage()); } } }

    В консольном терминале появится следующий вывод:

    Внимание! Щас вывалится исключение! Просто исключение. Без объяснения причин. Кто-то против?

    Нужен сервер для практики? Арендуйте VPS/VDS в России — root-доступ, NVMe, DDoS-защита и поддержка 24/7.

    VPS для проекта

    VPS с root-доступом, NVMe и поддержкой 24/7 на Simple-Server.

    StarterVDS

    490

    в месяц

    1 ядро

    1 ГБ RAM

    20 ГБ NVMe

    • 1 IPv4
    • KVM
    • Root-доступ
    • Безлимитный трафик
    Заказать VPS
    Рекомендуем

    PerformanceVDS

    1190

    в месяц

    2 ядра

    4 ГБ RAM

    60 ГБ NVMe

    • 1 IPv4
    • KVM
    • Root-доступ
    • Базовая DDoS-защита
    Заказать VPS

    Нужна другая конфигурация или чистый VPS без панели?

    Все тарифы VPS

    Похожие статьи, которые могут быть вам интересны