Механизм исключений значительно упрощает создание больших надежных программ, уменьшает объем необходимого кода и повышает уверенность в том, что в приложении не будет необработанной ошибки. Освоить работу с исключениями несложно, и это. одна из языковых возможностей, способных принести немедленную и значительную выгоду в ваших проектах. В этой главе будет показано, как правильно организовать обработку исключений в программе, а также как сгенерировать собственное исключение, если какой-то из ваших методов сталкивается с непредусмотренными трудностями.
Основные исключения
Простейшим примером является деление. Потенциальное деление на нуль может быть выявлено проверкой соответствующего условия. Но что делать, если знаменатель оказался нулем? Возможно, в контексте текущей задачи известно, как следует поступить с нулевым знаменателем. Но, если нулевой знаменатель возник неожиданно, деление в принципе невозможно, и тогда необходимо возбудить исключение, а не продолжать исполнение программы.
При возбуждении исключения происходит сразу несколько вещей. Во-пер-вых, создается объект, представляющий исключение, — точно так же, как и любой другой объект в Java (в куче, оператором new). Далее текущий поток исполнения (тот самый, где произошла ошибка) останавливается, и ссылка на объект, представляющий исключение, извлекается из текущего контекста. С этого момента включается механизм обработки исключений, который начинает поиск подходящего места программы для передачи исключения. Таким местом является
В качестве простого примера выдачи исключения представьте ссылку на объект t. Возможно, полученная вами ссылка не была инициализирована; стоит проверить это обстоятельство, прежде чем вызывать методы с использованием этой ссылки. Чтобы передать информацию об ошибке на более высокий уровень, создайте объект, представляющий передаваемую информацию, и «запустите» его из текущего контекста. Тем самым вы
if(t — null)
throw new NullPointerException( );
Вырабатывается исключение, которое позволяет вам — в текущем контексте — переложить с себя ответственность, не задумываясь о будущем. Ошибка, словно по волшебству, обрабатывается где-то в другом месте (вскоре мы узнаем, где именно).
Один из основополагающих аспектов исключений состоит в том, что при возникновении нежелательных ситуаций выполнение программы не продолжается по обычному пути. Исключения позволяют вам (в крайнем случае) остановить программу и сообщить о возникших трудностях или (в идеале) разобраться с проблемой и вернуть программу в стабильное состояние.
Аргументы исключения
Исключения, как и любые объекты Java, создаются в куче оператором new, который выделяет память и вызывает конструктор. У всех стандартных исключений существует два конструктора: стандартный (по умолчанию) и другой, со строковым аргументом, в котором можно разместить подходящую информацию об исключении:
throw new NullPointerExceptionC't = null");
Переданная строка потом может быть извлечена различными способами, о чем будет рассказано позже.
Ключевое слово throw влечет за собой ряд довольно интересных действий. Как правило, сначала new используется для создания объекта, представляющего условие происшедшей ошибки. Ссылка на указанный объект передается команде throw. Фактически этот объект «возвращается» методом, несмотря на то что для возвращаемого объекта обычно предусмотрен совсем другой тип. Таким образом, упрощенно можно говорить об обработке исключений как об альтернативном механизме возврата из исполняемого метода (впрочем, с этой аналогией не стоит заходить слишком далеко). Возбуждение исключений также позволяет выходить из простых блоков видимости. В обоих случаях возвращается объект исключения и происходит выход из текущего метода или блока.
Но все сходство с обычным возвратом из метода на этом заканчивается, поскольку при возврате из исключения вы попадаете совсем не туда, куда попали бы при нормальном вызове метода. (Обработчик исключения может находиться очень «далеко» — на расстоянии нескольких уровней в стеке вызова — от метода, где возникла исключительная ситуация.)