- Обработка исключений в Java: Полное руководство с лучшими и наихудшими практиками
- Обзор
- Что такое Обработка исключений?
- Зачем использовать обработку исключений?
- Иерархия исключений
- Проверенные Исключения
- Непроверенные Исключения
- Ошибки
- Как обрабатывать исключения
- бросок и броски
- попробуйте-поймайте блоки
- наконец, Блоки
- заявление о попытке использования ресурсов
- Несколько блоков захвата
- Блоки захвата соединения
- Как создавать исключения
- Создание проверенного исключения
- Git Essentials
- Создание непроверенного исключения
- Бросание
- Обертывание
- Бросание бросаемого или _Exception*?
- Наследование исключений
- Лучшие и худшие методы обработки исключений
- Лучшие Методы Обработки Исключений
- Избегайте Исключительных Условий
- Используйте попытку с ресурсами
- Закройте ресурсы в try-catch-наконец-то
- Наихудшие Методы Обработки Исключений
- Глотание Исключений
- Вернитесь в последний блок
- Бросаю в последний блок
- Имитация оператора goto
- Лесозаготовки и метания
- Перехват исключения или Выбрасывание
- Вывод
Обработка исключений в Java: Полное руководство с лучшими и наихудшими практиками
Автор: David Landup
Дата записи
Обзор
Обработка исключений в Java-одна из самых основных и фундаментальных вещей, которые разработчик должен знать наизусть. К сожалению, это часто упускается из виду, а важность обработки исключений недооценивается – это так же важно, как и остальная часть кода.
В этой статье давайте рассмотрим все, что вам нужно знать об обработке исключений в Java, а также о хороших и плохих методах.
Что такое Обработка исключений?
Мы ежедневно сталкиваемся с обработкой исключений в реальной жизни.
При заказе товара в интернет – магазине-товара может не быть в наличии на складе или может произойти сбой в доставке. Таким исключительным условиям можно противостоять, изготовив другой продукт или отправив новый после сбоя в доставке.
При создании приложений – они могут столкнуться со всевозможными исключительными условиями. К счастью, будучи опытным в обработке исключений, таким условиям можно противостоять, изменив поток кода.
Зачем использовать обработку исключений?
При создании приложений мы обычно работаем в идеальной среде – файловая система может предоставить нам все файлы, которые мы запрашиваем, наше подключение к Интернету стабильно, а JVM всегда может предоставить достаточно памяти для наших нужд.
К сожалению, на самом деле окружающая среда далека от идеала – файл не может быть найден, время от времени прерывается подключение к Интернету, а JVM не может предоставить достаточно памяти, и мы остаемся с пугающей Ошибкой StackOverflow .
Если мы не справимся с такими условиями, все приложение окажется в руинах, а весь остальной код устареет. Поэтому мы должны уметь писать код, который может адаптироваться к таким ситуациям.
Представьте, что компания не может решить простую проблему, возникшую после заказа продукта, – вы не хотите, чтобы ваше приложение работало таким образом.
Иерархия исключений
Все это просто напрашивается вопрос – каковы эти исключения в глазах Java и JVM?
В конце концов, исключения-это просто объекты Java, которые расширяют интерфейс Throwable :
Когда мы говорим об исключительных условиях, мы обычно имеем в виду одно из трех:
- Проверенные Исключения
- Непроверенные Исключения/Исключения во Время Выполнения
- Ошибки
Примечание : Термины “Время выполнения” и “Непроверенный” часто используются взаимозаменяемо и относятся к одному и тому же типу исключений.
Проверенные Исключения
Проверенные исключения-это исключения, которые мы обычно можем предвидеть и планировать заранее в нашем приложении. Это также исключения, которые компилятор Java требует, чтобы мы либо обрабатывали, либо объявляли при написании кода.
Правило обработки или объявления относится к нашей ответственности за то, чтобы либо объявить, что метод создает исключение в стеке вызовов, не предпринимая особых усилий для его предотвращения, либо обработать исключение с помощью нашего собственного кода, что обычно приводит к восстановлению программы из исключительного состояния.
По этой причине они называются проверенными исключениями . Компилятор может обнаружить их до выполнения, и вы знаете об их потенциальном существовании во время написания кода.
Непроверенные Исключения
Непроверенные исключения-это исключения, которые обычно возникают из-за человеческой, а не экологической ошибки. Эти исключения проверяются не во время компиляции, а во время выполнения, поэтому их также называют Исключениями во время выполнения .
Им часто можно противостоять, реализуя простые проверки перед сегментом кода, который потенциально может быть использован таким образом, чтобы сформировать исключение во время выполнения, но об этом позже.
Ошибки
Ошибки-это самые серьезные исключительные условия, с которыми вы можете столкнуться. От них часто невозможно избавиться, и нет реального способа справиться с ними. Единственное, что мы, как разработчики, можем сделать, – это оптимизировать код в надежде, что ошибки никогда не возникнут.
Ошибки могут возникать из-за ошибок человека и окружающей среды. Создание бесконечно повторяющегося метода может привести к ошибке StackOverflowError , или утечка памяти может привести к ошибке OutOfMemoryError .
Как обрабатывать исключения
бросок и броски
Самый простой способ устранить ошибку компилятора при работе с проверенным исключением – просто выбросить ее.
Мы обязаны пометить подпись нашего метода предложением throws . Метод может добавлять столько исключений, сколько необходимо в его предложении throws , и может добавлять их позже в коде, но это не обязательно. Этот метод не требует оператора return , даже если он определяет тип возвращаемого значения. Это связано с тем, что по умолчанию он создает исключение, которое резко прекращает поток метода. Поэтому оператор return будет недоступен и вызовет ошибку компиляции.
Имейте в виду, что любой, кто вызывает этот метод, также должен следовать правилу обработки или объявления.
При создании исключения мы можем либо создать новое исключение, как в предыдущем примере, либо пойманное исключение.
попробуйте-поймайте блоки
Более распространенным подходом было бы использовать try – catch блок для перехвата и обработки возникающего исключения:
В этом примере мы “отметили” опасный сегмент кода, заключив его в блок try . Это говорит компилятору, что мы знаем о потенциальном исключении и намерены обработать его, если оно возникнет.
Этот код пытается прочитать содержимое файла, и если файл не найден, исключение FileNotFoundException | поймано и повторно обработано . Подробнее на эту тему позже.
Запуск этого фрагмента кода без допустимого URL-адреса приведет к появлению исключения:
В качестве альтернативы, мы можем попытаться оправиться от этого состояния вместо того, чтобы бросать:
Запуск этого фрагмента кода без действительного URL-адреса приведет к:
наконец, Блоки
Вводя новый тип блока, блок наконец выполняется независимо от того, что происходит в блоке try. Даже если он внезапно завершится созданием исключения, блок finally будет выполнен.
Это часто использовалось для закрытия ресурсов, открытых в блоке try , поскольку возникающее исключение пропускало бы код, закрывающий их:
Однако этот подход был осужден после выпуска Java 7, которая представила лучший и более чистый способ закрытия ресурсов, и в настоящее время рассматривается как плохая практика.
заявление о попытке использования ресурсов
Ранее сложный и подробный блок может быть заменен:
Это намного чище и, очевидно, упрощено, если включить объявление в круглые скобки блока try .
Кроме того, вы можете включить в этот блок несколько ресурсов, один за другим:
Таким образом, вам не нужно беспокоиться о закрытии ресурсов самостоятельно, так как блок try-with-resources гарантирует, что ресурсы будут закрыты в конце инструкции.
Несколько блоков захвата
Когда код, который мы пишем, может выдавать более одного исключения, мы можем использовать несколько блоков catch для обработки их по отдельности:
Когда блок try вызывает исключение, JVM проверяет, является ли первое пойманное исключение подходящим, и если нет, продолжает, пока не найдет его.
Примечание : При перехвате общего исключения будут перехвачены все его подклассы, поэтому не требуется перехват их отдельно.
Перехват FileNotFound исключения в этом примере не требуется, поскольку он распространяется из Исключение IOException , но если возникнет необходимость, мы сможем поймать его до Исключения IOException :
Таким образом, мы можем обрабатывать более конкретное исключение иным способом, чем более общее.
Примечание : При перехвате нескольких исключений компилятор Java требует, чтобы мы помещали более конкретные исключения перед более общими, иначе они были бы недоступны и привели бы к ошибке компилятора.
Блоки захвата соединения
Чтобы уменьшить шаблонный код, в Java 7 также были введены блоки union catch/|. Они позволяют нам одинаково обрабатывать несколько исключений и обрабатывать их исключения в одном блоке:
Как создавать исключения
Иногда мы не хотим обрабатывать исключения. В таких случаях мы должны заботиться только о том, чтобы генерировать их, когда это необходимо, и позволять кому-то другому, вызывающему наш метод, обрабатывать их соответствующим образом.
Создание проверенного исключения
Когда что-то идет не так, например, количество пользователей, подключающихся в настоящее время к нашему сервису, превышает максимальную сумму, которую сервер может легко обработать, мы хотим создать исключение, чтобы указать на исключительную ситуацию:
Git Essentials
Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!
Этот код будет увеличивать количество Пользователей до тех пор, пока не превысит максимально рекомендуемую сумму, после чего он выдаст исключение. Поскольку это проверяемое исключение, мы должны добавить предложение throws в подпись метода.
Определить подобное исключение так же просто, как написать следующее:
Создание непроверенного исключения
Создание исключений во время выполнения обычно сводится к проверке входных данных, поскольку они чаще всего возникают из – за неправильного ввода-либо в виде исключения IllegalArgumentException , NumberFormatException , ArrayIndexOutOfBoundsException , либо NullPointerException :
Поскольку мы создаем исключение runtimeexception, нет необходимости включать его в подпись метода, как в приведенном выше примере, но это часто считается хорошей практикой, по крайней мере, для документации.
Опять же, определить пользовательское исключение во время выполнения, подобное этому, так же просто, как:
Бросание
Создание исключения упоминалось ранее, поэтому вот краткий раздел для уточнения:
Повторное выбрасывание относится к процессу выбрасывания уже пойманного исключения, а не к выбрасыванию нового.
Обертывание
Обертывание, с другой стороны, относится к процессу обертывания уже пойманного исключения в другое исключение:
Бросание бросаемого или _Exception*?
Эти классы верхнего уровня могут быть пойманы и переосмыслены, но как это сделать, может варьироваться:
В этом случае метод создает исключение NumberFormatException , которое является исключением во время выполнения. Из-за этого нам не нужно отмечать подпись метода с помощью NumberFormatException или Throwable .
Однако, если мы создадим проверенное исключение в методе:
Теперь мы должны объявить, что метод выбрасывает Бросаемый . Почему это может быть полезно-это широкая тема, которая выходит за рамки данного блога, но для этого конкретного случая есть способы использования.
Наследование исключений
Подклассы, наследующие метод, могут создавать только меньшее количество проверенных исключений, чем их суперкласс:
При таком определении следующий метод вызовет ошибку компилятора:
Лучшие и худшие методы обработки исключений
Учитывая все это, вы должны быть хорошо знакомы с тем, как работают исключения и как их использовать. Теперь давайте рассмотрим лучшие и худшие методы, когда дело доходит до обработки исключений, которые, мы надеемся, теперь полностью понятны.
Лучшие Методы Обработки Исключений
Избегайте Исключительных Условий
Иногда, используя простые проверки, мы можем вообще избежать формирования исключений:
Вызов этого метода с допустимым индексом приведет к:
Но вызов этого метода с индексом, который выходит за рамки, приведет к:
В любом случае, даже если индекс слишком высок, нарушающая строка кода не будет выполнена, и никаких исключений не возникнет.
Используйте попытку с ресурсами
Как уже упоминалось выше, всегда лучше использовать более новый, более лаконичный и чистый подход при работе с ресурсами.
Закройте ресурсы в try-catch-наконец-то
Если вы по какой-либо причине не используете предыдущий совет, по крайней мере, не забудьте закрыть ресурсы вручную в блоке “Наконец”.
Я не буду включать пример кода для этого, так как оба они уже были предоставлены для краткости.
Наихудшие Методы Обработки Исключений
Глотание Исключений
Если вы намерены просто удовлетворить компилятор, вы можете легко сделать это, проглотив исключение :
Проглатывание исключения относится к акту перехвата исключения и не устранения проблемы.
Таким образом, компилятор удовлетворен, так как исключение поймано, но вся соответствующая полезная информация, которую мы могли извлечь из исключения для отладки, потеряна, и мы ничего не сделали для восстановления после этого исключительного условия.
Еще одна очень распространенная практика-просто распечатать трассировку стека исключения:
Такой подход создает иллюзию управляемости. Да, хотя это лучше, чем просто игнорировать исключение, распечатав соответствующую информацию, это не справляется с исключительным условием больше, чем игнорирование.
Вернитесь в последний блок
Если выполнение блока try завершается внезапно по какой-либо другой причине R, то наконец блок выполняется, и тогда есть выбор.
Итак, в терминологии документации, если блок наконец завершается нормально, то оператор try завершается внезапно по причине R.
Если блок finally завершается внезапно по причинам, то оператор try завершается внезапно по причинам (и причина R отбрасывается).
По сути, при резком возврате из блока finally JVM удалит исключение из блока try , и все ценные данные из него будут потеряны:
В этом случае, даже если блок try создает новый Исключение IOException , мы используем return в блоке finally , резко завершая его. Это приводит к внезапному завершению блока try из-за оператора return, а не из-за исключения IOException , что, по сути, приводит к удалению исключения в процессе.
Бросаю в последний блок
Очень похоже на предыдущий пример, использование throw в наконец блоке приведет к удалению исключения из try-catch блока:
В этом примере исключение MyException , созданное внутри блока finally , затмит исключение, созданное блоком catch , и вся ценная информация будет удалена.
Имитация оператора goto
Критическое мышление и творческие способы поиска решения проблемы-хорошая черта, но некоторые решения, какими бы творческими они ни были, неэффективны и избыточны.
Java не имеет оператора goto , как некоторые другие языки, а скорее использует метки для перехода по коду:
И все же некоторые люди используют исключения для их имитации:
Использование исключений для этой цели неэффективно и медленно. Исключения предназначены для исключительного кода и должны использоваться для исключительного кода.
Лесозаготовки и метания
При попытке отладить фрагмент кода и выяснить, что происходит, не регистрируйте и не создавайте исключение:
Выполнение этого является избыточным и просто приведет к появлению множества сообщений журнала, которые на самом деле не нужны. Объем текста уменьшит видимость журналов.
Перехват исключения или Выбрасывание
Почему бы нам просто не перехватить исключение или Выбрасываемое, если оно улавливает все подклассы?
Если нет веской, конкретной причины поймать кого-либо из этих двоих, обычно не рекомендуется этого делать.
Перехват Исключения будет перехватывать как проверенные, так и исключения во время выполнения. Исключения во время выполнения представляют собой проблемы, которые являются прямым результатом проблемы программирования, и поэтому их не следует перехватывать, поскольку нельзя разумно ожидать, что они будут устранены или обработаны.
Ловить Бросаемый поймает все . Это включает в себя все ошибки, которые на самом деле никоим образом не должны быть пойманы.
Вывод
В этой статье мы рассмотрели исключения и обработку исключений с нуля. После этого мы рассмотрели лучшие и худшие методы обработки исключений в Java.
Надеюсь, вы нашли этот блог информативным и образовательным, счастливого кодирования!
Источник