Исключения в Java. Часть 2
Учимся футболить исключения и исследуем их различия на практике.
Иллюстрация: Dana Moskvina / Skillbox Media
Из этой статьи вы узнаете:
- как пробросить исключение наверх;
- когда и зачем это делать;
- о checked- и unchecked-исключениях;
- о хоткеях, ускоряющих обработку исключений.
А сперва советуем перечитать первую часть.
Проброс исключений
Ранее мы рассказали, что все исключения в Java — это классы, наследники Throwable и Exception; мельком взглянули на класс Error, от которого наследуются ошибки уровня Java-машины.
Сегодня поговорим только про семейство Exception.
В Java много встроенных исключений. На каждом останавливаться не будем: вы и так познакомитесь с ними на практике. Сейчас важнее до конца уяснить, как с исключениями работать.
Напомню, что есть три способа обойтись с исключением:
- Ничего не делать.
- Обработать в конструкции try-catch.
- Пробросить наверх.
С первыми двумя мы разбирались здесь. Остался проброс исключений наверх.
Наверх — это куда? Это выше по стеку вызовов. То есть если метод A вызвал метод B, метод B вызвал метод C, а методC пробросил исключение наверх, то оно всплывёт до метода B — и станет его проблемой.
Как пробросить исключение?
Проще простого.
Вернёмся к примеру из предыдущей статьи. Помните, там был метод hereWillBeTrouble, который мог вызвать исключение ArithmeticException?
Чтобы пробросить это исключение, достаточно добавить в сигнатуру метода hereWillBeTrouble ключевое слово throws, а после него — название класса пробрасываемого исключения.
Вот так:
Теперь метод hereWillBeTrouble объявляет всему миру, что может кинуть исключение ArithmeticException.
Посмотрим, что произойдёт, если он исполнит эту угрозу.
Изначально исключение возникнет напротив стрелки 1. Но throws передаст его в строку, на которую указывает стрелка 2.
Исключение передалось как эстафетная палочка. И теперь источником исключения является не только строка int oops = 42 / 0, но и строка hereWillBeTrouble();.
Что нам дал проброс исключения?
- Так мы говорим разработчику, который вызывает метод hereWillBeTrouble, что в нём может возникнуть исключение типа ArithmeticException.
- В вызывающем метод hereWillBeTrouble коде это исключение можно обработать с помощью конструкции try-catch.
Вернёмся к первому блоку кода (в котором нет конструкции try-catch).
Вы наверняка заметили, что и без throws код работал точно так же. Стектрейс (разбирали его здесь) не изменился, исключение в итоге бросалось пользователю.
Но теперь разработчики, которые используют метод hereWillBeTrouble, предупреждены и, если нужно, обезопасят свой код с помощью try-catch.
И это всё, что дало слово throws?
В случае с непроверяемыми исключениями — да. Но вот в случае с проверяемыми… пристегнитесь, нас ждёт новый поворот в деле об исключениях!
Проверяемые и непроверяемые исключения
Все исключения в Java делятся на две группы. Зовутся они checked и unchecked («проверяемые» и «непроверяемые»). Иногда их также называют «обрабатываемые» и «необрабатываемые».
Запомните! Проверяемые исключения обязательно нужно обрабатывать либо пробрасывать. Непроверяемые — по желанию.
Как понять, проверяемое исключение или нет?
Разработчики языка Java решили так: исключения, которые наследуются от RuntimeException, — непроверяемые, а все остальные — проверяемые.
Исследуем различия на практике
Раз ArithmeticException — непроверяемое исключение, то его можно обрабатывать, а можно и не обрабатывать. Мы обрабатывали.
Когда при выполнении кода возникало исключение — программа вылетала, это само собой. Но заметьте: программа компилировалась и запускалась даже с риском возникновения исключения.
Без проблем компилируется и код, в котором кидают исключение явно:
Компиляция успешна только потому, что исключение непроверяемое.
А теперь проделаем то же самое с проверяемым исключением:
Вжух — и наш код перестал компилироваться!
Вот мы и подтвердили разницу между проверяемыми и непроверяемыми исключениями: нельзя скомпилировать и запустить программу, если в ней есть проверяемое исключение, которое кинули, но не обработали и не пробросили.
Итак, вариантов у нас два:
- обработать исключение с помощью try-catch;
- пробросить исключение выше.
Оборачивать код конструкцией try-catch вы уже умеете. Рассмотрим второй вариант:
А код всё ещё не компилируется. Но если в первом случае ошибка компиляции возникала в методе hereWillBeTrouble — в строке, где кидалось проверяемое, но при этом необработанное исключение, то теперь ошибка будет в методе main, потому что проверяемое и необработанное исключение поднялось до него.
Теперь исключение возникает в строке, где вызывается метод hereWillBeTrouble. Повторимся, так происходит потому, что этот метод кидает проверяемое исключение (он продекларировал это с помощью ключевого слова throws).
И теперь методу main тоже нужно что-то с исключением делать: либо обработать его с помощью try-catch, либо пробросить ещё выше.
Первый вариант, try-catch:
Второй вариант, проброс выше:
Несмотря на то что метод main запускается первым, ему тоже разрешено кидать исключения.
Что мы выяснили про проброс исключения наверх?
В случае с непроверяемыми исключениями это способ передать свою проблему тем, кто будет вызывать наш метод.
Наш метод теперь компилируется, а вот вызывающий — нет. Так будет до тех пор, пока в нём не сделают что-то с исключением (возможно, передадут его ещё выше по стеку вызовов).
Компилятор и throws
Интересный момент. Если метод способен кинуть проверяемое исключение, то компилятор не будет перепроверять: ему всё равно, что происходит внутри этого метода. Для него главное, что метод о такой возможности заявил.
Убедитесь, что следующий код не скомпилируется:
Горячие клавиши
Напоследок — небольшая подсказка.
Среда разработки стремится упростить нам жизнь: когда в коде обнаруживается проверяемое исключение — она предлагает реализовать его обработку.
Нам остаётся только выбрать, чего мы хотим: обработать исключение с помощью try-catch или пробросить выше.
Горячие клавиши, которые позволяют это сделать:
- в среде IntelliJ IDEA — Alt + Enter / ⌥Enter,
- для среды разработки Eclipse — F2.
Например:
IntelliJ IDEA:
Eclipse:
Что дальше?
В следующей статье я расскажу обо всей мощи конструкции try-catch.
Ещё больше хитростей, дженериков и других особенностей Java — на курсе «Профессия Java-разработчик». Научим программировать на самом востребованном языке и поможем устроиться на работу.