JavaRush /Курсы /Java Syntax /Знакомство с исключениями

Знакомство с исключениями

Java Syntax
9 уровень , 3 лекция
Открыта

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

1. Программа пытается записать файл на заполненный диск.

2. Программа пытается вызвать метод у переменной, которая хранит ссылку – null.

3. Программа пытается разделить число на 0.

Все эти действия приводят к возникновению ошибки. Обычно это приводит к закрытию программы — продолжать выполнять дальше код не имеет смысла.

— Почему?

— А есть ли смысл крутить руль, если машина слетела с трассы и падает с обрыва?

— Программа что, должна завершиться?

— Да. Раньше так и было. Любая ошибка приводила к завершению программы.

— Это очень разумный подход.

— А разве не лучше было бы попробовать работать дальше?

— Ага. Ты набрал большущий текст в Word’е, сохранил его, он не сохранился, но программа говорит тебе, что все в порядке. И ты продолжаешь набирать его дальше. Глупо, да?

— Ага.

— Потом разработчики придумали интересный ход: каждая функция возвращала статус своей работы. 0 означал, что она отработала как надо, любое другое значение – что произошла ошибка: это самое значение и было кодом ошибки.

— Но был у такого подхода и минус. После каждого(!) вызова функции нужно было проверять код (число), который она вернула. Во-первых, это было неудобно: код по обработке ошибок исполнялся редко, но писать его нужно было всегда. Во-вторых, функции часто сами возвращают различные значения – что делать с ними?

— Ага. Я тоже об этом подумал.

— Но потом наступило светлое будущее — появились исключения и механизм обработки ошибок. Вот как это работает:

1. Когда возникает ошибка, Java-машина создаёт специальный объект – exception – исключение, в который записывается вся информация об ошибке. Для разных ошибок есть разные исключения.

2. Затем это «исключение» приводит к тому, что программа тут же выходит из текущей функции, затем выходит из следующей функции, и так пока не выйдет из метода main. Затем программа завершается. Еще говорят, что Java-машина «раскручивает назад стек вызовов».

— Но ты же сказала, что теперь программа не обязательно завершается.

— Верно, потому что есть способ перехватить исключение. В нужном месте, для нужных нам исключений мы можем написать специальный код, который будет перехватывать эти исключения и что-то делать. Важное.

— Для этого есть специальная конструкция try-catch. Вот как это работает:

Вот пример программы, которая перехватывает исключение – деление на 0. И продолжает работать.
public class ExceptionExample2
{
    public static void main(String[] args)
    {
        System.out.println("Program starts");

        try
        {
            System.out.println("Before method1 calling");
            method1();
            System.out.println("After method1 calling. Never will be shown");
        }
        catch (Exception e)
        {
           System.out.println("Exception has been caught");
        }

        System.out.println("Program is still running");
    }

    public static void method1()
    {
        int a = 100;
        int b = 0;
        System.out.println(a / b);
    }
}
Вот что будет выведено на экран:

«Program starts»
«Before method1 calling»
«Exception has been caught»
«Program is still running»

— А почему не будет выведено «After method1 calling. Never will be shown»?

— Рада, что ты спросил. В строчке 25 у нас было деление на ноль. Это привело к возникновению ошибки – исключения. Java-машина создала объект ArithmeticException с информацией об ошибке. Этот объект является исключением.

— Внутри метода method1 возникло исключение. И это привело к немедленному завершению этого метода. Оно привело бы и к завершению метода main, если бы не было блока try-catch.

— Если внутри блока try возникает исключение то, оно захватывается в блоке catch. Остаток кода в блоке try, не будет исполнен, а сразу начнётся исполнение блока catch.

— Как-то не очень понятно.

— Другими словами этот код работает так:

1. Если внутри блока try возникло исключение, то код перестаёт исполняться, и начинает исполняться блок catch.

2. Если исключение не возникло, то блок try исполняется до конца, а catch никогда так и не начнёт исполняться.

— Гм?

— Представь, что после вызова каждого метода мы проверяем: завершился ли только что вызванный метод сам по себе или в результате исключения. Если исключение было, тогда мы переходим на исполнение блока catch, если он есть, и захватываем исключение. Если блока catch нет, то завершаем и текущий метод. Тогда такая же проверка начинается в том методе, который вызвал нас.

— Теперь вроде понятно.

— Вот и отлично.

— А что значит Exception внутри catch?

Все исключения – это классы, унаследованные от класса Exception. Мы можем перехватить любое из них, указав в блоке catch его класс, или все сразу, указав общий родительский класс — Exception. Затем из переменной e (эта переменная хранит ссылку на объект исключения), можно получить всю необходимую информацию о возникшей ошибке.

— Круто! А если в моем методе могут возникнуть разные исключения, можно обрабатывать их по-разному?

— Не можно, а нужно. Сделать это можно вот так:

Пример:
public class ExceptionExample2
{
    public static void main(String[] args)
    {
        System.out.println("Program starts");

        try
        {
            System.out.println("Before method1 calling");
            method1();
            System.out.println("After method1 calling. Never will be shown ");
        }
        catch (NullPointerException e)
        {
           System.out.println("Reference is null. Exception has been caught");
        }
        catch (ArithmeticException e)
        {
            System.out.println("Division by zero. Exception has been caught");
        }
        catch (Exception e)
        {
            System.out.println("Any other errors. Exception has been caught");
        }

        System.out.println("Program is still running");
    }

    public static void method1()
    {
        int a = 100;
        int b = 0;
        System.out.println(a / b);
    }
}

— Блок try может содержать несколько блоков catch, каждый из которых будет захватывать исключения своего типа.

— Гм. Ну, вроде понятно. Сам такого не напишу, конечно, но если в коде встречу – пугаться не буду.

9
Задача
Java Syntax Pro, 9 уровень, 3 лекция
Недоступна
Импорты: Part 1
В классе Solution замени все короткие имена классов в коде на полные.
9
Задача
Java Syntax Pro, 9 уровень, 3 лекция
Недоступна
Импорты: Part 2
В классе Solution замени все полные имена классов в коде на короткие.
Комментарии (207)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Basilius Уровень 33
6 апреля 2023

public class MaybeNextTime {
            public static void main(String[] args) {
                System.out.println("Гм. Ну, вроде понятно. Сам такого не напишу, конечно");

                try {
                    System.out.println("но если в коде встречу");
                    NashalnikePohvalil();
                    System.out.println("мне подняли зарплатку");
                } catch (GnevPointerException nashalnikeVGneve) {
                    System.out.println("Скажу, что это был не я");
                }
                System.out.println("Тишина...");

                public static void NashalnikePohvalil (); {
                    System.out.println("Э, кто написал эту хрень!?");
                }
            }
        }
xxxx Уровень 23
11 января 2023
Сам ничего не написал, встретил в коде. Испугался.
AMIGO_2022_11_09 Уровень 17
5 декабря 2022
Сам такого не напишу, конечно, но если в коде встречу – пугаться не буду
AMIGO_2022_11_09 Уровень 17
5 декабря 2022
"Сам такого не напишу, конечно, но если в коде встречу – пугаться не буду." так и живём)
21 февраля 2022
Уже был этот вопрос, но никто почему то не ответил. Спрошу ещё разок) Т.е. мы уже на стадии написания метода должны знать, что он выкинет исключение? Или получив на стадии исполнения метода ошибку начать её отлавливать?
Стас Уровень 41
23 февраля 2022
Да, на стадии написания кода ты должен примерно понимать где у тебя может появится Exception. Например когда ты получаешь с клавиатуры строку и переводишь её в число ты должен понимать что пользователь может ввести строку состоящую не только из чисел и при парсинге произойдет Exception. Так что мы должны окружить конструкцией try / catch строку String str = Integer.parseInt(reader.readLine());
Иван Уровень 23
18 июля 2022
Очень долго этого ждал, когда уже можно будет парсить число без ошибок! Наконец то эта тем!!! И Вам спасибо за разъяснение!
Рыбалко Иван Уровень 36
15 февраля 2022
Кирилл Уровень 15
16 декабря 2021
Тут есть несколько важных моментов, которые неочевидны новичкам и из написанной статьи, но необходимы для хорошего стиля программирования и нормальной предсказуемой работы программы: 1) В блоке try должен быть только тот код, который бросает исключения. Не надо тащить туда всё подряд. Обернули в try нужный вызов, поймали исключение в catch, потом оставшийся код который исключений не бросает. В идеале try/catch вынести в отдельный вызов 2) Исключения нужно ловить как можно более узкого типа. Если вызов может бросить ArithmeticException и только его, не надо ловить общий тип Exception 3) В идеале исключение нужно ловить в том классе, где вызываем метод\функцию которая его бросает. Очень плохая практика бросить исключение и через 10 уровней вложенности наверху его ловить. В этом случае если в метод добавится исключение или наоборот он перестанет его бросать придется изменить все эти 10 вложенных классов которые его "пробрасывали" наверх
Damiankain Уровень 19
27 декабря 2021
Т.е. мы заранее должны знать, что код будет бросать исключения? Или как к этому приходят?
Евгения Уровень 12
4 февраля 2022
Хочется верить, что настанет тот день, когда я пойму весь глубокий смысл прочитанного 🙄
Кирилл Уровень 15
28 февраля 2022
Нет, заранее ничего знать не должны, это может породить ошибку преждевременной оптимизации Не очень понял о чем конкретно вопрос, так мог бы подробнее ответить
Grock Уровень 44
14 сентября 2022
А мы можем отследить место ошибки ()пакет, класс, номер строки словив ее с помощью catch? Как тогда это можно сделать?
Benjam1nBTN Уровень 24
15 сентября 2022

Затем из переменной e (эта переменная хранит ссылку на объект исключения), можно получить всю необходимую информацию о возникшей ошибке.
4 ноября 2022
Да, мы заранее предугадываем те места, где может произойти ошибка. Примеров много. Делим на что-то - это что-то может быть 0. Берем элемент массива, где порядковый номер переменная - можем выйти за пределы массива. Хотим получить с клавиатуры число - а пользователь ввел строку и тд, все это можно предусмотреть. Можно делать проверки, например: если делитель не 0, то поделить, если 0, то начать ругаться. Или: если та фигня, которую ввел пользователь, содержит только цифры, то скорее всего это распарсится в число, а если нет, то опять начать ругаться. Но такой код - это моветон. Красивее, ловить ошибки в одном месте. В одном месте чистый код, в другом - какие то действия в случае ошибки, мухи с котлетами отдельно.
Anonymous #2454324 Уровень 1
14 января 2023
Я читал, что хорошим тоном является создание отдельного класса для обработки исключений. Из ваших слов, получается что это не совсем так. Я про третий пункт говорю. Поясните, пожалуйста
Константин Уровень 23
14 февраля 2023
Думаю автор имел в виду, что когда пишется код, который может выкинуть exeption, есть вариант не только это исключение обработать, но и прокинуть в методы, которые его вызвали. Но и там нам лениво его обрабатывать, мы прокидываем это исключение дальше(точнее наоборот раньше по стеку) - таким образом мы создаем ситуацию, когда исключение "кочует" обратно по стеку вызовов и приводит к фейлу уже где придется)))) и начинается разбор где и что упало. это ладно, если такую дичь написал ты сам - сам и колупайся, а если кто-то использует ваш код как фреймворк? я понял это так. абсолютно согласен. Ну и на наличие/отсутствие классов обработки этот пункт не влияет - техническая реализация тут не затронута, хочешь класс добавь и им обработай, хочешь бери чей-то класс из библиотек - хочешь руками обрабатывай в catch
Aleksei Reinsalu Уровень 19
20 ноября 2021
Когда все слишком понятно, надо добавлять физические упражнения в занятия. А то от искушения "все додумать за минуту" нервы горят и идет отрыв от реальности. Очнитесь, даже простые вещи нужно думать на комфортной скорости.
Andrey Panchenko Уровень 26
22 июля 2021
Иногда Амиго говорит, что ему понятно, но мне не понятно, а сейчас всё наоборот.
Юличка Уровень 32
15 июня 2021
пока что мозг как у хлебушка...
Инна Уровень 29
24 августа 2021
а как сейчас?))
Юличка Уровень 32
26 августа 2021
так до сих пор и не дошла полноценно до этой темы
Ars Уровень 41
12 сентября 2021
А теперь?))
Aleksandr Gorohov Уровень 28
22 сентября 2021
уже и мне интересно
ктулху Уровень 27
9 октября 2021
дошла?)
Антон Уровень 23
2 ноября 2021
Ответь нам)
Dmitry Tsybankov Уровень 23
4 ноября 2021
Видимо мы останемся без ответа...
Игорь Уровень 29
17 июня 2022
мне до сих пор интересно
Иван Уровень 23
18 июля 2022
ну как там?
Разобралась наконец-таки в теме?
Grock Уровень 44
14 сентября 2022
Видимо разобралась... и мозг стал сухариком, обретшим твердую структуру нейронных связей, образующих новое сакральное знание :)
красиво написал...)