JavaRush /Java блог /Java Developer /Исключения (Exception): checked, unchecked и свои собстве...
Автор
Владимир Портянко
Java-разработчик в Playtika

Исключения (Exception): checked, unchecked и свои собственные

Статья из группы Java Developer
Привет! В прошлой лекции мы познакомились с таким аспектом языка Java как исключения и увидели примеры работы с ними. Сегодня мы глубже рассмотрим их структуру, а также научимся писать собственные исключения :)

Виды исключений

Как мы и говорили, классов-исключений в Java очень много, почти 400! Но все они делятся на группы, так что запомнить их довольно легко. Вот как это выглядит: Исключения: checked, unchecked и свои собственные - 2У всех исключений есть общий класс-предок Throwable. От него происходят две большие группы — исключения (Exception) и ошибки (Error). Error — это критическая ошибка во время исполнения программы, связанная с работой виртуальной машины Java. В большинстве случаев Error не нужно обрабатывать, поскольку она свидетельствует о каких-то серьезных недоработках в коде. Наиболее известные ошибки: StackOverflowError — возникает, например, когда метод бесконечно вызывает сам себя, и OutOfMemoryError — возникает, когда недостаточно памяти для создания новых объектов. Как видишь, в этих ситуациях чаще всего обрабатывать особо нечего — код просто неправильно написан и его нужно переделывать. Exceptions — это, собственно, исключения: исключительная, незапланированная ситуация, которая произошла при работе программы. Это не такие серьезные ошибки, как Error, но они требуют нашего внимания. Все исключения делятся на 2 вида — проверяемые (checked) и непроверяемые (unchecked). Исключения: checked, unchecked и свои собственные - 3Все проверяемые исключения происходят от класса Exception. Что значит “проверяемые”? Мы частично упоминали об этом в прошлой лекции: “...компилятор Java знает о самых распространенных исключениях, и знает, в каких ситуациях они могут возникнуть”. Например, он знает, что если программист в коде считывает данные из файла, может легко возникнуть ситуация, что файл не существует. И таких ситуаций, которые он может заранее предположить, очень много. Поэтому компилятор заранее проверяет наш код на наличие потенциальных исключений. Если он их найдет, то не скомпилирует код, пока мы не обработаем их или не пробросим наверх. Второй вид исключений — “непроверяемые”. Они происходят от класса RuntimeException. Чем же они отличаются от проверяемых? Казалось бы, тоже есть куча разных классов, которые происходят от RuntimeException и описывают конкретные runtime-исключения. Разница в том, что этих ошибок компилятор не ожидает. Он как бы говорит: “На момент написания кода я ничего подозрительного не обнаружил, но при его работе что-то пошло не так. Видимо, в коде есть ошибки!” И это действительно так. Непроверяемые исключения чаще всего являются следствием ошибок программиста. А компилятор явно не в силах предусмотреть все возможные неправильные ситуации, которые люди могут создать своими руками :) Поэтому он не будет проверять обработку таких исключений в нашем коде. Ты уже сталкивался с несколькими непроверяемыми исключениями:
  • ArithmeticException возникает при делении на ноль
  • ArrayIndexOutOfBoundsException возникает при попытке обратиться к ячейке за пределами массива.
Теоретически, конечно, создатели Java могли бы ввести обязательную обработку таких исключений, но во что бы тогда превратился код? При любой операции деления чисел пришлось бы писать try-catch для проверки — не на ноль ли ты случайно делишь? При любом обращении к массиву надо было бы писать try-catch чтобы проверить, не вышел ли ты за эти пределы. Любой написанный код представлял бы собой спагетти и был бы совершенно нечитаемым. Логично, что от этой идеи отказались. Поэтому непроверяемые исключения не нужно обрабатывать в блоках try-catch или пробрасывать наверх, хотя технически это возможно, как и с Error.

Как выбросить свое исключение

Разумеется, создатели Java не в состоянии предусмотреть все исключительные ситуации, которые могут возникнуть в программах. Программ в мире слишком много, и они слишком разные. Но это не страшно, поскольку при необходимости ты можешь создавать собственные исключения. Это делается очень легко. Тебе достаточно создать собственный класс. Его название должно заканчиваться на “Exception”. Компилятору это не нужно, но читающим твой код программистам сразу будет понятно, что это класс-исключение. Кроме того, нужно указать что класс происходит от класса Exception. Это уже нужно для компилятора и корректной работы. Например, у нас есть класс Собака — 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 не удосужились добавить в свой язык специальное исключение для неправильно экипированных собак, мы исправили их оплошность :)
Комментарии (181)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Максим Li Уровень 36
8 декабря 2023
Отличная статья, добавил в коллекцию)
Кру Уровень 21
24 октября 2023
с котами было привычнее )) и судя по лекции - хозяевам котов живется легче )))
Kirill Уровень 22
7 сентября 2023
Каждый раз читаю комментарии и, постоянно, не понимаю недовольство людей, по поводу подачи материала. По мне, наоборот, круто что лекции и доп. материал идут уже в конце. Пока решаешь задачи, приходится самостоятельно искать информацию и разбираться. Зато, когда доходишь до сюда и читаешь доп. материал, сразу все встает на свои места.
Dmitry Vidonov Уровень 29 Expert
16 августа 2023
Excellent job, Mr. Portyanko!
Alexander Rozenberg Уровень 32
20 июля 2023
fine
Rustam Уровень 35 Student
15 июля 2023
бедные собаки
Skotique Уровень 35
29 июня 2023
экипируйте собак правильно!
No Name Уровень 32
11 июня 2023
+ статья в копилку
Ислам Уровень 33
2 июня 2023
Nice
Kobanidze Уровень 28
15 марта 2023
"Как видишь, ничего сложного. " )))