Skip to content
Aleksandr Kuchuk edited this page Jun 12, 2016 · 11 revisions

###Введение Часто на собеседованиях спрашивают про Exceptions и их обработку. Поэтому решил написать про это.

В нашем приложении иногда(а возможно и часто) происходят какие-то ошибки. Что же с ними делать? Можно их либо игнорировать, либо возвращать некий код ошибки, а после уже расшифровывать его, либо, как в Java, использовать механизм Exceptions.

Exceptions

Очень распространенная штука это - Exceptions, но что это такое? На пальцах - Exception это сигнал о нестандартной ситуации. Например - мы не можем закрыть файл или удалить что-то, не хватает памяти и т.д Ниже мы видим иерархию исключений, корень - java.lang.Throwable и два класса - java.lang.Exception и java.lang.Error

Ну а java.lang.Exception это super class для java.lang.RuntimeException java.lang.Throwable и java.lang.Exceptionэто так называемые checked exceptions(Проверяемые), java.lang.RuntimeException и java.lang.Error это unchecked. Checked exceptions проверяются компилятором в compile time. И их мы обязаны обработать. В то время как RE(Runtime Exceptions) мы не обязаны явно перехватывать и обрабатывать, ну на то они и runtime:)

Мы также можем перехватывать исключения. И перехватывать мы можем все виды исключений.

Но тогда зачем нам столько видов исключений? Давайте подумаем - какая разница между Error и Exception?

Error vs Exception

Error - это более серьезная ситуация, нежели Exception. Т.е если происходит что-то такое, что мы либо не можем исправить, либо это крайне сложно починить, например, у нас закончилась память или мы вызываем несуществующий метод - это вот Error. Т.е непросто исключительная ситуация - а прямо рядом с паникой:) Но если мы не можем закрыть файл, мы делаем какой-нибудь запрос, не можем строку к число преобразовать - это ситуации, после которых мы можем продолжить работать, мы можем перехватить это, обработать(например - попросить пользователя ввести еще раз число) - это java.lang.Exception.

Т.е разница - в логическом разделении.

Exceptions, more

Теперь посмотрим глубже на Exceptions - java.lang.Exception and java.lang.RuntimeException. RuntimeException - это исключения падающие в Runtime, т.е когда мы уже работаем с нашим приложением.

Надо понимать, что Exceprions - это class, т.е мы туда можем положить свою логику, методы и переменные, если надо. Однако кидать исключения - довольно дорогая операция, поэтому не надо просто так разбрасываться ими.

Когда что кидать?

  • java.lang.Exception Это ситуации, которые нам не подконтрольны, т.е не смогли закрыть файл, не смогли дессериализовать класс - мы ничего не можем сделать, но можем отреагировать. Опять же мы обязаны такие исключения обрабатывать. Либо прокидывать вверх с помощью throws, в сигнатуру метода. Метод может декларировать сколько угодно исключений.

  • java.lang.RuntimeException А вот тут уже - это наши ошибки, ошибки разработчика скорее. Т.е обращаемся к null, делим на ноль где-то и прочее. Мы можем эти ошибки перехватывать, а можем пропускать. Ловить такие ошибки постоянно - не совсем правильно наверное, так как мы можем так и не понять причину ошибок и падений.

  • java.lang.Error Критические ошибки, после которых мы не можем или с трудом можем продолжать работу. Как и все остальные - мы можем такие ошибки ловить, но зачем? Ловить их можно только в случае, если мы можем или знаем как нам поступить в таких ситуациях.

Кидать мы можем тоже любые исключения.

How to handle

Ловить и обрабатывать ошибки - try/catch/finally.

Расположение catch blocks - важно. Пусть у нас метод бросает IOException и Exception, мы пишем что-то типа такого:

try {
method();
} catch (Exception e) {//do some logic 1}
catch (IOException e) {//do some logic 2}

И видим, что IOException обработка недостижима, мы более широкий фильтр "отлова" установили выше.

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

Отсюда понятно, что если нам надо перехватить прямо вот все, то надо ловить Throwable:

try {
method();
} catch (Throwable t) {//do some logic 1}

Отметим, что в таком подходе мы ловим и RE, и Error!

Ловим RE:

try {
   String numberAsString = "one";
   Double res = Double.valueOf(numberAsString);
    } catch (RuntimeException re) {
            System.out.println("Error while convert string to double!");
    }

Ловим Error:

        try {
            throw new Error();
        } catch (RuntimeException re) {
            System.out.println("RE");
        } catch (Error error) {
            System.out.println("ERROR");
        }
    }

Все вроде бы ясно и понятно. Теперь вот еще что.

Полезно иногда иметь свою иерархию Exceptions. Например, у нас есть method2() и он кидает целых 3 разных Exception-а. Имея свою иерархию мы просто пишем 3 catch block-а на каждое исключение и обрабатываем как нам надо. Без этого у нас был бы только один catch block с перехватом Exception, где мы уже будем понимать что мы вообще перехватили и как это обрабатывать.

Идем дальше.

А что если наше исключение прерывает поток(interrupt thread) - все просто используем Thread.UncaughtExceptionHandler.

И еще раз! Аккуратнее с обработкой исключений.

public static void main(String[] args){
        try {
            try {
                throw new Exception("0");
            } finally {
                if (true) {
                    throw new IOException("1");
                }
                System.err.println("2");
            }
        } catch (IOException ex) {
            System.err.println(ex.getMessage());
        } catch (Exception ex) {
            System.err.println("3");
            System.err.println(ex.getMessage());
        }
    }

Видим на выходе "1"!

####Вывод

  • Используем обрабатываемые исключения в случае, когда мы понимаем, что тут может быть ошибка.
  • Не обрабатываемое исключение - если это наша ошибка.
  • Полезно иметь свою иерархию исключений.
  • Используйте finally, если работаете с ресурсами и try-with-resources
  • Кидаем исключения с помощью throw
  • Поднять исключение в метод - throws
  • Все проверяемые исключения обязаны быть отловлены!
Clone this wiki locally