Обработка исключений и конструкция try...catch...finally

Последнее обновление: 07.10.2025

Нередко в процессе выполнения программы могут возникать ошибки, при том необязательно по вине разработчика. Некоторые из них трудно предусмотреть или предвидеть, а иногда и вовсе невозможно. Так, например, может неожиданно оборваться сетевое подключение при передаче файла. Подобные ситуации называются исключениями.

В языке Java предусмотрены специальные средства для обработки подобных ситуаций. Одним из таких средств является конструкция try...catch...finally. При возникновении исключения в блоке try управление переходит в блок catch, который может обработать данное исключение. Если такого блока не найдено, то пользователю отображается сообщение о необработанном исключении, а дальнейшее выполнение программы останавливается. И чтобы программа не упала из-за возникшей ошибки, применяется блок try..catch..finally:

try{
     
}
catch (Тип_исключения переменная){
     
}
finally{
     
}

При применении блока try...catch..finally вначале выполняются все инструкции в блоке try. Если в этом блоке не возникло исключений, то после его выполнения начинает выполняться блок finally. И затем конструкция try..catch..finally завершает свою работу.

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

Если нужный блок catch

Применение конструкции try..catch..finally

Рассмотрим простеший пример:

class Program{
     
	public static void main(String[] args) {
         
        int a = 5;
        int b = 0;
        int result = a / b;
        System.out.printf("Результат: %d\n", result);
    }
}

В данном случае происходит деление числа на 0, что приведет к генерации исключения. В итоге мы увидим на консоли следующую картину:

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at Program.main(Program.java:7)

Таким образом, консоль нас проинформирует, что в процессе выполнения программы произошло исключение типа java.lang.ArithmeticException и снабдит сообщение куцей информацией об исключении: "by zero". Тем не менее по этому сообщению мы можем понять, что проблема в делении на ноль. Кроме того, мы видим, что непосредственно проблема возникла в методе Program.main, который располагается в файле Program.java на строке 7. Это облегчает поиск проблемого участка кода в исходном файле.

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

class Program{
     
	public static void main(String[] args) {
         
        divide(10, 2);
        divide(10, 0);
    }

    static void divide(int a, int b){

        if(b != 0) System.out.println(a / b);
        else System.out.println("Деление на ноль");
    }
}

Но проблема заключается в том, что не все ситуации мы можем предотвратить таким образом. Поэтому посмотрим, как мы можем применить конструкцию try...catch...finally, чтобы избежать подобного аварийного завершения программы:

class Program{
     
	public static void main(String[] args) {
         
        divide(10, 0);
        System.out.println("Конец программы");
    }

    static void divide(int a, int b){

        try{

            int result = a / b;
            System.out.printf("Результат: %d\n", result);
        }
        catch(Throwable _){

            System.out.println("Возникло исключение!");
        }
        finally{

            System.out.println("Блок finally");
        }
    }
}

Рассмотрим основные моменты. Прежде всего у нас есть некоторый код, который потенциально может вызвать исключение:

int result = a / b;

Поэтому его помещаем блок try.

try{

    int result = a / b;
    System.out.printf("Результат: %d\n", result);
}

Если в блоке try вдруг возникает исключение (при b = 0), то обычный порядок выполнения останавливается и переходит к инструкции сatch. Выражение catch имеет следующий синтаксис:

catch (тип_исключения имя_переменной){

    // выполняемые инструкции при возвникновении исключения
}

После оператора catch в скобках указывается тип исключения и затем имя переменной этого исключения (то есть как и при объявлении переменной). Если возникшее исключение соответствует этому типу, то выполняется данный блок catch.

В нашем случае в качестве типа исключения использован максимально широкий тип, который соответствует любому исключению - это класс Throwable:

catch(Throwable _){

    System.out.println("Возникло исключение!");
}

Таким образом, обработка исключения типа Throwable предполагает обработку любого исключения, в том числе деления на ноль. Но здесь в целях упрощения примера нам пока переменная этого исключения не важна, мы ее никак не используем, поэтому вместо имени переменной я использовал прочерк. А в самом блоке catch просто выводится диагностическое сообщение.

И после блока catch идет блок finally, который также просто выводит сообщение на консоль.

finally{

    System.out.println("Блок finally");
}

Выполнение try..catch..finally

Посмотрим выполнение выше определенной конструкции try..catch..finally на двух примерах: при упешном и неудачном выполнении программы. Возьмем первую ситуацию - когда делитель равен 0:

public static void main(String[] args) {
         
    divide(10, 0);
    System.out.println("Конец программы");
}

Поскольку делитель равен 0, и очевидно возникнет ошибка, соответственно программа не сможет выполнить деление, и мы полуим следующий консольный вывод:

Возникло исключение!
Блок finally
Конец программы

Если смотреть по инструкциям, то ход программы выглядит следующим образом:

  1. divide(10, 0);

    Пеореходим в метод divide

  2. int result = a / b;

    Поскольку b равно 0, переход в блок catch

  3. System.out.println("Возникло исключение!");

    Переход в блок finally

  4. System.out.println("Блок finally");

    Блок finally отработал, метод divide завершил свое выполнение, поэтому возвращаемся в мтеод main

  5. System.out.println("Конец программы");

    Последняя инструкция метода main выполнена, программа завершена

Теперь посмотрим на другую ситуацию - когда делитель не равен 0:

public static void main(String[] args) {
         
    divide(10, 2);
    System.out.println("Конец программы");
}

Эта программа успешно выполнит деление, и мы полуим следующий консольный вывод:

Результат: 5
Блок finally
Конец программы

Если смотреть по инструкциям, то ход программы выглядит следующим образом:

  1. divide(10, 2);

    Пеореходим в метод divide

  2. int result = a / b;

    Поскольку b равно 2, выполняется деление, а переменная result получает значение 5

  3. System.out.printf("Результат: %d\n", result);

    Блок try отработал, поэтому переходим в блок finally

  4. System.out.println("Блок finally");

    Блок finally отработал, метод divide завершил свое выполнение, поэтому возвращаемся в мтеод main

  5. System.out.println("Конец программы");

    Последняя инструкция метода main выполнена, программа завершена

Сокращенная версия конструкции try..catch..finally

При использовании конструкции try..catch..finally обязательным блоком является только блок try. Один из остальных двух блоков можно опустить и оставить либо catch (если блок не нужен, либо . Например, часто блок не используется, и используются только try..catch:

static void divide(int a, int b){

    try{

        int result = a / b;
        System.out.printf("Результат: %d\n", result);
    }
    catch(Throwable _){

        System.out.println("Возникло исключение!");
    }
}

Можно оставить блок finally, но в этом случае при возникновении исключения оно не будет обработано, и программа упадет с ошибкой:

static void divide(int a, int b){

    try{

        int result = a / b; 
        System.out.printf("Результат: %d\n", result);
    }
    finally{

        System.out.println("Блок finally");
    }
}

Возвращение результата из try..catch..finally

Внутри конструкции try..catch..finally может использоваться оператор return для возвращения результата из метода. Например:

static int parse(String s)
{
    try
    {
        return Integer.parseInt(s);
    }
    catch(Throwable _){

        System.out.println("Ошибка преобразования");
        return 0;
    }
 }

Здесь в блоке try возвращается результат метода Integer.parseInt(). Этот метод преобразует строку в число. Однако если строку нельзя конвертировать в число, то возникает ошибка. С помощью блока catch мы перехватываем ошибку и возвращаем число 0. Хотя здесь спорный момент: стоит ли возвращать значение при ошибке, а если возвращать, то какое. И Java для подобных ситуаций имеет более гибкие механизмы. Но возьмем данный пример за основу.

Далее в программе мы можем вызвать метод parse и получить из него результат:

class Program{
     
	public static void main(String[] args) throws Exception {
         
        int result = parse("5"); 
        System.out.println(result);     // 5

        result = parse("t");            // Ошибка преобразования
        System.out.println(result);     // 0
    }

    static int parse(String s)
    {
        try
        {
            return Integer.parseInt(s);
        }
        catch(Throwable _){

            System.out.println("Ошибка преобразования");
            return 0;
        }
    }
}

Но стоит учитывать, что, если конструкция try содержит блок finally, то этот блок обязательно выполняется. И если блок finally также содержит оператор return, то он маскирует исходное возвращаемое значение. Например

class Program{
     
	public static void main(String[] args) throws Exception {
         
        int result = parse("5");
        System.out.println(result);        // -1

        result = parse("t");                // Ошибка преобразования 
        System.out.println(result);        // -1
    }

    static int parse(String s)
    {
        try
        {
            return Integer.parseInt(s);
        }
        catch(Throwable _){

            System.out.println("Ошибка преобразования");
            return 0;
        }
        finally{
            return -1;
        }
    }
}

Здесь блок finally возвращает значение -1. И вне зависимости от того, успешно было ли преобразование или нет, метод все равно возвратит -1, так как после блока try и catch обязательно выполняется блок finally

Обработка исключений и условные конструкции

Стоит отметить, что конструкция try..catch на низком уровне довольно дорогая операция. И если ряд исключительных ситуаций может быть предвиден разработчиком, то лучше обойтись без try..catch, а подобные ситуации обработать с помощью условных конструкций. Как, например, в случае с делением на 0 предпочтительнее проверить второе число на 0, чем использовать конструкцию try..catch.

Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850
Morty Proxy This is a proxified and sanitized view of the page, visit original site.