Java: исключения и их обработка
Любая программа будет работать стабильно только в том случае, если её исходный код отлажен, и в нем отсутствуют условия, которые могут вызывать непредвиденные ситуации. Процесс отлова возможных сбоев выполняется на стадии программирования. Для этого разработчик учитывает все предполагаемые исходы и пытается ограничить действие ошибки таким образом, чтобы она не смогла нарушить работу программы или привести к её краху.
Когда может понадобиться обработка исключений
В Java исключения могут быть вызваны в результате неправильного ввода данных пользователем, отсутствия необходимого для работы программы ресурса или внезапного отключения сети. Для комфортного использования созданного разработчиком приложения, необходимо контролировать появление внештатных ситуаций. Потребитель не должен ждать завершения работы зависшей программы, терять данные в результате необработанных исключений или просто часто появляющихся сообщений о том, что что-то пошло не так.
Обработка исключений Java
Что необходимо учитывать? Язык Java обладает своим встроенным функционалом обработки исключений. Конечно же большой процент ошибок отлавливается ещё на стадии компиляции, когда система автоматически сообщит о том, что использовать её дальше невозможно. Но существует и такой вид исключений, который возникает во время работы программы. Разработчик должен суметь предвидеть это и спроектировать код таким образом, чтобы он не вызвал ошибки, а обработал её особым способом или передал управление в другую ветку.
В Java такой отлов исключений навязывается компилятором, поэтому типичные проблемы известны и имеют свою стандартную схему исполнения.
Типичные исключения
Самым простым примером, при котором можно получить исключение — это деление. Несмотря на всю его простоту, в выражении, в качестве делителя, может оказаться ноль, что приведёт к ошибке. Хорошо, если его появление можно предсказать ранее и предотвратить. Но такой вариант доступен не всегда, поэтому отлов исключения нужно организовать непосредственно при возникновении «деления на ноль».
В Java механизм обработки перехвата ошибки выглядит так:
- в куче создаётся объект исключения, так же как и любой другой;
- естественный ход программы прерывается;
- механизм исключения пытается найти альтернативный способ продолжения кода;
- найдя место безопасного исполнения программы в обработчике, работа либо восстановится, либо произойдёт реализация исключения особым способом.
Простейший пример создания ошибки может выглядеть таким образом:
if (a == null)
throw new NullPointerException();
Здесь переменная a проверяется на инициализацию, т.е. не равна ли ссылка на объект null. В случае, если такая ситуация возникла и нужна особая обработка, выбрасывается исключение с помощью throw new NullPointerException().
Немного подробностей о ключевых словах
При работе с исключениями часто приходится использовать ключевые слова Java для обозначения того или иного действия. В данном языке программирования их пять:
- Try. Это ключевое слово уже встречалось и означает оно переход в участок кода, который может сгенерировать исключение. Блок ограничивается фигурными скобками {}.
- Catch. Перехватывает нужный тип исключения и обрабатывает его соответствующим образом.
- Finally. Данное ключевое слово является дополнительным и служит для выполнения некоего участка кода, который необходим в любом случае, даже если ни одно исключение не перехвачено. Добавляется непосредственно после блока try.
- Throw — позволяет создавать исключения Java в любом месте кода.
- Throws — ключевое слово, которое ставится в сигнатуре метода. Оно означает, что последующий код может выбросить исключение Java указанного типа. Такая метка служит сигналом для разработчиков, что нужно иметь в виду — метод может сработать не так, как от него ожидают.
Отлов с помощью try
Выброс в Java исключения, естественно предполагает, что оно будет особым образом обработано. Удобнее всего это сделать, если участок кода отгорожен в некий блок. Который возможно содержит исключение. При выполнении такого кода виртуальная машина найдёт непредвиденную ситуацию, поймёт, что находится в критическом блоке и передаст управление в участок с обработкой.
В Java код заворачивается в специальный блок try, внутри которого может быть сгенерировано исключение. Таким образом, в него помещается сразу несколько непредвиденных ситуаций, которые будут отловлены в одном месте, не расползаясь по коду.
Самый типичный код с блоком обработки выглядит так:
try {
//Здесь будет определён код, который может породить исключение
} catch (Тип_исключения_1 идентификатор_1) {
//Здесь происходит обработка исключения, согласно его типу и условиям;
} catch (Тип_исключения_2 идентификатор_2) {
//Здесь происходит обработка исключения, согласно его типу и условиям;
}
Ключевое слово catch сообщает о том, что код, подвергнутый проверке на исключение, нужно обработать так, как описано далее, при условии, что он соответствует его типу. Идентификатор может использоваться внутри блока кода обработки как аргументы.
Finally
Как стало понятно из предыдущей главы, блоки catch ловят исключения и обрабатывают их. Но очень часто возникает ситуация, когда должен выполниться некий код вне зависимости от того, были ли отловлены ошибки. Для этого существует ключевое слово finally. Оно применяется для увеличения значений различных счётчиков, закрытия файлов или соединений с сетью.
В данном участке представлены несколько блоков catch с придуманными методами отлова исключений. К примеру, код, содержащийся в try порождает непредвиденную ситуацию типа Cold. Тогда в консоль будут выведены выражения «Caught cold!» и «Is that something to cheer about?». То есть блок finally выполняется в любом случае.
На самом деле способ избежать запуска finally существует. Связан он с завершением работы виртуальной машины. Найти, как это реализовать, можно на просторах сети Интернет.
Ключевое слово throw
Throw генерирует исключение. Его синтаксис выглядит так:
throw new NewException();
Здесь создаётся новое исключение с типом NewException(). В качестве типа могут использоваться уже входящие в стандартные библиотеки Java классы и определённые ранее разработчиком собственного производства.
Такая конструкция входит в описание какого-либо метода, вызов которого затем должен происходить в рамках блока try, для того, чтобы была возможность его перехватить.
Ключевое слово throws
Что делать, если в процессе разработки возникла ситуация, когда метод может сгенерировать исключение, но не в состоянии правильно обработать. Для этого в сигнатуре метода указывается слово throws и тип возможного исключения.
Эта метка является своеобразным указателем для клиентских разработчиков о том, что метод не способен обработать своё же исключение. К тому же, если тип ошибки является проверяемым, то компилятор заставит явно это указать.
Try с ресурсами
В Java версии 7 разработчики включили такое важное нововведение, как обработка блока try с ресурсами.
Многие создаваемые объекты в Java, после их использования должны быть закрыты для экономии ресурсов. Раньше приходилось это учитывать и останавливать такие экземпляры вручную. Теперь же в них появился интерфейс AutoClosable. Он помогает автоматически закрывать уже использованные объекты, помещённые в блок try. Благодаря такому подходу писать код стало удобней, в его читаемость значительно повысилась.
Собственные классы исключений Java
Создатели описываемого языка программирования учли многие аспекты при проектировании типов непредвиденных ситуаций. Однако, все варианты исхода событий предотвратить не получится, поэтому в Java реализована возможность определения своих собственных исключений, подходящих именно под нужды конкретного кода.
Простейший способ создания — унаследовать от наиболее подходящего к контексту объекта.
Здесь произошло наследование от Exception, класса, который используется для определения собственных исключений. В MyException имеются два конструктора — один по умолчанию, второй — с аргументом msg типа String.
Затем в public классе FullConstructors реализован метод f, сигнатура которого содержит throws MyException. Это ключевое слово означает, что f может выбросить исключение Java типа MyException. Далее в теле метода производится вывод текстовой информации в консоль и собственно сама генерация MyException, посредством throw.
Второй метод немного отличается от первого тем, что при создании исключения, ему передается строковый параметр, который будет отражён в консоли при отлове. В main видно, что f() и g() помещены в блок проверки try, а ключевое слово catch настроено на отлов MyException. Результатом обработки будет вывод сообщения об ошибке в консоль:
Таким образом получилось добавить исключения Java, созданные собственноручно.
Архитектура исключений
Как и все объекты в Java, исключения также наследуются и имеют иерархическую структуру. Корневым элементом всех ошибок, выбрасываемых в этом языке программирования является класс java.lang.Throwable. От него наследуются два вида — Error и Exception.
Error — оповещает о критических ошибках и представляет собой непроверяемые исключения Java. Перехват и обработка таких данных в большинстве случаев происходит на стадии разработки и не нуждается во внедрении в код конечного приложения.
Наиболее часто используемым классом для создания и анализа исключений служит Exception. Который, в свою очередь, делится на несколько веток, в том числе RuntimeException. К RuntimeException относятся исключения времени выполнения, то есть происходящие во время работы программы. Все унаследованные от него классы являются непроверяемыми.
Часто встречаемые исключения
В Java исключения, список которых представлен ниже, используются наиболее часто, поэтому стоит описать каждый из них подробней:
- ArithmeticException. Сюда входят ошибки связанные с арифметическими операциями. Самый яркий пример — деление на ноль.
- ArrayIndexOutOfBoundsException — обращение к номеру элемента массива, который превышает общую его длину.
- ArrayStoreException — попытка присвоить элементу массива несовместимого типа.
- ClassCastException — попытка неправильного приведения одного типа к другому.
- IllegalArgumentException — использование неправильного аргумента в вызове метода.
- NegativeArraySizeException — исключение при создании массива отрицательного размера.
- NullPointerException — неправильное использование ссылки на null.
- NumberFormatException — возникает при неверном преобразовании строки в число.
- UnsupportedOperationException — операция не поддерживается.
Данные примеры представляют собой непроверяемые типы исключений Java. А вот так выглядят проверяемые:
- ClassNotFoundException — класс не обнаружен.
- IllegalAcccessException — ограничение доступа к классу.
- InterruptedException — прерывание работы потока.
- NoSuchFieldException — не существует требуемое поле.
Интерпретация исключений
Говоря о часто встречаемых исключениях нужно отметить, что их интерпретация в ходе разработки, может быть воспринята неправильно. Далее идёт небольшой список, поясняющий более подробно, когда может возникнуть непредвиденная ситуация.
NullPointerException. Самым первым случаем, когда возникает исключение, является обращение к ссылке на объект, которая равна null. Также это распространяется на методы нулевого экземпляра класса. NullPointerException может быть брошен и в случае получения длины массива равной null. Избежать таких ситуаций поможет периодическая проверка объектов на null.
ArrayIndexOutOfBoundsException. Любая программа не может существовать без использования массивов. Соответственно, частое обращение к ним может порождать и ошибки. Возникает исключение, когда разработчик пытается обратиться к элементу массива, который отсутствует в списке индексов. Например, запрашиваемое значение выше длины или меньше нуля. Очень часто появляется в результате того, что счёт в массиве начинается с нуля.
Выводы
Обработка исключений Java — мощный инструмент среды, который значительно облегчает работу программиста и позволяет ему создавать чистый и лишенный ошибок код. От того, насколько плавно и стабильно функционирует приложение, зависит статус и репутация компании-разработчика.
Конечно, в более или менее простых программах отследить внештатные ситуации гораздо проще. А вот в больших автоматизированных комплексах на несколько сотен тысяч строк такое возможно только в результате проведения длительной отладки и тестирования.
За Java исключения, ошибки от которых возникают в некоторых приложениях, отдельные компании предлагают вознаграждение при их нахождении энтузиастами. Особенно ценятся те, которые вызывают нарушение политики безопасности программного комплекса.