Привет! В прошлой лекции мы познакомились с таким аспектом языка Java как исключения и увидели примеры работы с ними.
Коротко: все исключения наследуются от класса
У всех исключений есть общий класс-предок
Все проверяемые исключения происходят от класса
Throwable, который делится на Error (критические ошибки JVM) и Exception. Исключения бывают проверяемые (checked, от Exception) и непроверяемые (unchecked, от RuntimeException). Свое исключение создают классом, который наследует Exception, и выбрасывают словом throw.
Сегодня мы глубже рассмотрим их структуру, а также научимся писать собственные исключения :)
Кратко
- Все исключения наследуются от класса
Throwable, который делится наError(критические ошибки JVM) иException. - Checked (проверяемые) исключения происходят от
Exception: компилятор требует их обработать или пробросить. - Unchecked (непроверяемые) происходят от
RuntimeException: компилятор их не проверяет, обычно это ошибки программиста. Error(напримерStackOverflowError,OutOfMemoryError) обрабатывать обычно не нужно.- Свое исключение создают классом, который наследует
Exception(имя оканчивается на Exception). - Исключение выбрасывают словом
throw, а в сигнатуре метода указываютthrows.
Виды исключений
Как мы и говорили, классов-исключений в Java очень много, почти 400! Но все они делятся на группы, так что запомнить их довольно легко. Вот как это выглядит:Throwable.
От него происходят две большие группы: исключения (Exception) и ошибки (Error).
Error это критическая ошибка во время исполнения программы, связанная с работой виртуальной машины Java. В большинстве случаев Error не нужно обрабатывать, поскольку она свидетельствует о каких-то серьезных недоработках в коде.
Наиболее известные ошибки: StackOverflowError возникает, например, когда метод бесконечно вызывает сам себя, и OutOfMemoryError возникает, когда недостаточно памяти для создания новых объектов.
Как видишь, в этих ситуациях чаще всего обрабатывать особо нечего: код просто неправильно написан и его нужно переделывать.
Exceptions это, собственно, исключения: исключительная, незапланированная ситуация, которая произошла при работе программы.
Это не такие серьезные ошибки, как Error, но они требуют нашего внимания.
Все исключения делятся на 2 вида: проверяемые (checked) и непроверяемые (unchecked).
Exception. Что значит "проверяемые"?
Мы частично упоминали об этом в прошлой лекции:
"...компилятор Java знает о самых распространенных исключениях, и знает, в каких ситуациях они могут возникнуть".
Например, он знает, что если программист в коде считывает данные из файла, может легко возникнуть ситуация, что файл не существует. И таких ситуаций, которые он может заранее предположить, очень много.
Поэтому компилятор заранее проверяет наш код на наличие потенциальных исключений. Если он их найдет, то не скомпилирует код, пока мы не обработаем их или не пробросим наверх.
Второй вид исключений это "непроверяемые".
Они происходят от класса RuntimeException.
Чем же они отличаются от проверяемых? Казалось бы, тоже есть куча разных классов, которые происходят от RuntimeException и описывают конкретные runtime-исключения.
Разница в том, что этих ошибок компилятор не ожидает.
Он как бы говорит: "На момент написания кода я ничего подозрительного не обнаружил, но при его работе что-то пошло не так. Видимо, в коде есть ошибки!"
И это действительно так. Непроверяемые исключения чаще всего являются следствием ошибок программиста.
А компилятор явно не в силах предусмотреть все возможные неправильные ситуации, которые люди могут создать своими руками :) Поэтому он не будет проверять обработку таких исключений в нашем коде.
Ты уже сталкивался с несколькими непроверяемыми исключениями:
ArithmeticExceptionвозникает при делении на нольArrayIndexOutOfBoundsExceptionвозникает при попытке обратиться к ячейке за пределами массива.
| Признак | Проверяемые (checked) | Непроверяемые (unchecked) |
|---|---|---|
| Происходят от | Exception (кроме RuntimeException) | RuntimeException |
| Проверяет компилятор | да, требует обработки | нет |
| Нужно обрабатывать | да (try-catch или throws) | не обязательно |
| Обычная причина | внешние ситуации (файл не найден и т.п.) | ошибки в коде программиста |
| Примеры | FileNotFoundException, IOException | ArithmeticException, ArrayIndexOutOfBoundsException |
Как выбросить свое исключение
Разумеется, создатели Java не в состоянии предусмотреть все исключительные ситуации, которые могут возникнуть в программах. Программ в мире слишком много, и они слишком разные. Но это не страшно, поскольку при необходимости ты можешь создавать собственные исключения. Делается это в несколько шагов:- Создать новый класс, имя которого оканчивается на Exception (для читаемости кода).
- Унаследовать его от класса
Exceptionчерезextends Exception. - Добавить конструктор, который передает сообщение в конструктор родителя через
super(message). - В нужном месте выбросить исключение словом
throw new ВашеException(...). - В сигнатуре метода, который выбрасывает исключение, указать
throws ВашеException.
Exception. Это уже нужно для компилятора и корректной работы (про наследование через extends и вызов super подробнее в статье про конструкторы базовых классов).
Например, у нас есть класс Собака, Dog.
Мы можем погулять с собакой с помощью метода walk().
Но перед этим нам нужно проверить, надеты ли на нашего питомца ошейник, поводок и намордник. Если что-то из этого отсутствует, выбросим собственное исключение DogIsNotReadyException. Его код будет выглядеть так:
public class DogIsNotReadyException extends Exception {
public DogIsNotReadyException(String message) {
super(message);
}
}
Чтобы указать, что класс является исключением, нужно написать extends Exception после имени класса: это значит, что "класс происходит от класса Exception".
В конструкторе мы просто вызовем конструктор класса Exception со строкой message: она будет отображать пользователю сообщение от системы с описанием возникшей ошибки.
Вот как это будет выглядеть в коде нашего класса:
public class Dog {
String name;
boolean isCollarPutOn;
boolean isLeashPutOn;
boolean isMuzzlePutOn;
public Dog(String name) {
this.name = name;
}
public static void main(String[] args) {
}
public void putCollar() {
System.out.println("Ошейник надет!");
this.isCollarPutOn = true;
}
public void putLeash() {
System.out.println("Поводок надет!");
this.isLeashPutOn = true;
}
public void putMuzzle() {
System.out.println("Намордник надет!");
this.isMuzzlePutOn = true;
}
public void walk() throws DogIsNotReadyException {
System.out.println("Собираемся на прогулку!");
if (isCollarPutOn && isLeashPutOn && isMuzzlePutOn) {
System.out.println("Ура, идем гулять! " + name + " очень рад!");
} else {
throw new DogIsNotReadyException("Собака " + name + " не готова к прогулке! Проверьте экипировку!");
}
}
}
Теперь наш метод walk() выбрасывает исключение DogIsNotReadyException.
Это делается с помощью ключевого слова throw.
Как мы уже говорили ранее, исключение это объект. Поэтому в нашем методе при возникновении исключительной ситуации, на собаке чего-то не хватает, мы создаем новый объект класса DogIsNotReadyException и выбрасываем в программу с помощью слова throw.
К сигнатуре метода walk() мы прибавляем throws DogIsNotReadyException.
Иными словами, теперь компилятор в курсе, что вызов метода walk() может обернуться исключительной ситуацией.
Поэтому, когда мы вызовем это где-то в программе, исключение нужно будет обработать. Попробуем сделать это в методе main():
public static void main(String[] args) {
Dog dog = new Dog("Мухтар");
dog.putCollar();
dog.putMuzzle();
dog.walk();//Unhandled exception: DogIsNotReadyException
}
Не компилируется, исключение не обработано!
Обернем наш код в блок try-catch для обработки исключения:
public static void main(String[] args) {
Dog dog = new Dog("Мухтар");
dog.putCollar();
dog.putMuzzle();
try {
dog.walk();
} catch (DogIsNotReadyException e) {
System.out.println(e.getMessage());
System.out.println("Проверяем снаряжение! Ошейник надет? " + dog.isCollarPutOn + "\r\n Поводок надет? "
+ dog.isLeashPutOn + "\r\n Намордник надет? " + dog.isMuzzlePutOn);
}
}
Теперь посмотрим на вывод в консоль:
Ошейник надет!
Намордник надет!
Собираемся на прогулку!
Собака Мухтар не готова к прогулке! Проверьте экипировку!
Проверяем снаряжение! Ошейник надет? true
Поводок надет? false
Намордник надет? true
Посмотри, насколько более информативным стал вывод в консоль!
Мы видим каждый шаг, который происходил в программе; видим, в каком месте возникла ошибка, и сразу замечаем, чего именно не хватает нашей собаке :)
Вот так и создаются собственные исключения. Как видишь, ничего сложного. И хотя разработчики Java не удосужились добавить в свой язык специальное исключение для неправильно экипированных собак, мы исправили их оплошность :)
Больше о видах исключений можно прочитать в документации Oracle (Lesson: Exceptions).
Что еще почитать |
|---|
Исключения в Java (Базовый уровень - для начинающих) Как обрабатывать исключения в Java (Продвинутый уровень) |
Вопросы и ответы
Чем checked исключения отличаются от unchecked?
Checked (проверяемые) происходят отException, и компилятор требует их обработать или пробросить. Unchecked (непроверяемые) происходят от RuntimeException, компилятор их не проверяет.
Что такое Throwable, Exception и Error?
Throwable это общий класс-предок всех исключений. От него идут две ветки: Error (критические ошибки JVM, например нехватка памяти) и Exception (обычные исключения, которые требуют внимания).
Нужно ли обрабатывать unchecked исключения?
Не обязательно. Технически их можно поймать в try-catch, но компилятор этого не требует. Чаще всего они означают ошибку в коде, которую нужно исправить.Как создать собственное исключение в Java?
Создать класс с именем, оканчивающимся на Exception, унаследовать его отException и добавить конструктор с super(message). Затем выбрасывать его через throw.
Чем отличается throw от throws?
throw выбрасывает конкретный объект-исключение в коде. throws пишется в сигнатуре метода и перечисляет, какие исключения метод может выбросить.
Что такое Error и надо ли его обрабатывать?
Error это критическая ошибка виртуальной машины Java (например StackOverflowError, OutOfMemoryError). Обычно ее не обрабатывают: она означает серьезную проблему в коде, который нужно переделать.