Исключение представляет событие, которое возникает при выполнении программы и нарушает ее нормальной ход. Например, при передаче файла по сети может оборваться сетевое подключение, и в результате чего может быть сгенерировано исключение. Если исключение не обработано, то программа падает и прекращает свою работу. Поэтому при возникновении исключений их следует обрабатывать.
Для обработки исключений применяется конструкция try..catch..finally. В блок try помещаются те действия, которые потенциально могут вызвать исключение (например, передача файла по сети, открытие файла и т.д.). Блок catch перехватывает возникшее исключение и обрабатывает его. Блок finally выполняет некоторые завершающие действия.
try { // код, генерирующий исключение } catch (e: Exception) { // обработка исключения } finally { // постобработка }
После оператора catch в скобках помещается параметр, который представляет тип исключения. Из этого параметра можно получить информацию о произошедшем исключении.
Блок finally является необязательным, его можно опустить. Блок catch также может отсутствовать, однако обязательно должен быть блок try и как минимум один из блоков: либо catch, либо finally. Также конструкция может содержать несколько блоков catch для обработки каждого типа исключения, которое может возникнуть.
Блок catch выполняется, если только возникло исключение. Блок finally выполняется в любом случае, даже если нет исключения.
Например, при делении на ноль Kotlin генерирует исключение:
fun main() { try{ val n1 = 2 val n2 = 0 val result = n1 / n2 println(result) } catch(e: Exception){ println("Exception") } }
Действие, которое может вызвать исключение, то есть операция деления, помещается в блок try. В блоке catch перехватываем исключение. При этом каждое исключение имеет определенный тип. В данном случае используется общий тип исключений - класс Exception:
Exception
Если необходимы какие-то завершающие действия, то можно добавить блок finally
(например, если при работе с файлом возникает исключение, то в блоке finally
можно прописать закрытие файла):
try{ val n1 = 2 val n2 = 0 val result = n1 / n2 println(result) } catch(e: Exception){ println("Exception") } finally{ println("Program has been finished") }
В этом случае консольный вывод будет выглядеть следующим образом:
Exception Program has been finished
Базовый класс исключений - класс Exception
предоставляет ряд свойств, которые позволяют получить различную информацию об исключении:
message: сообщение об исключении
stackTrace: трассировка стека исключения - набор строк, где было сгенерировано исключение
Из функций класса Exception следует выделить функцию printStackTrace()
, которая выводит ту информацию, которая обычно отображается при необработанном исключении.
Применение свойств:
fun main() { try{ val n1 = 2 val n2 = 0 val result = n1 / n2 println(result) } catch(e: Exception){ println(e.message) for(line in e.stackTrace) { println("at $line") } } }
Консольный вывод программы:
/ by zero at AppKt.main(app.kt:5) at AppKt.main(app.kt)
Одна программа, один код может генерировать сразу несколько исключений. Для обработки каждого отдельного типа исключений можно определить отдельный блок catch. Например, при одном исключении мы хотим производить одни действия, при другом - другие.
try { val nums = arrayOf(1, 2, 3, 4) println(nums[6]) } catch(e:ArrayIndexOutOfBoundsException){ println("Out of bound of array") } catch (e: Exception){ println(e.message) }
В данном случае при доступе по недействительному индексу в массиве будет генерироваться исключение типа ArrayIndexOutOfBoundsException. С
помощью блока catch(e:ArrayIndexOutOfBoundsException)
. Если в программе будут другие исключения, которые не представляют тип ArrayIndexOutOfBoundsException,
то они будут обрабатываться вторым блоком catch, так как Exception - это общий тип, который подходит под все типы исключений. При этом стоит отметить,
что в начале обрабатывается исключение более частного типа - ArrayIndexOutOfBoundsException, и только потом - более общего типа Exception.
Возможно, в каких-то ситуациях мы вручную захотим генерировать исключение. Для генерации исключения применяется оператор throw, после которого указывается объект исключения
Например, в функции проверки возраста мы можем генерировать исключение, если возраст не укладывается в некоторый диапазон:
fun main() { val checkedAge1 = checkAge(5) val checkedAge2 = checkAge(-115) } fun checkAge(age: Int): Int{ if(age < 1 || age > 110) throw Exception("Invalid value $age. Age must be greater than 0 and less than 110") println("Age $age is valid") return age }
После оператора throw указан объект исключения. Для определения объекта Exception применяется конструктор, который принимает в качестве параметра сообщение об исключении. В данном случае это сообщение о некорректности введенного значения.
И если при вызове функции checkAge()
в нее будет передано число меньше 1 или больше 110, то будет сгенерировано исключение. Так, в данном случае
консольный вывод будет следующим:
Age 5 is valid Exception in thread "main" java.lang.Exception: Invalid value -115. Age must be greater than 0 and less than 110 at AppKt.checkAge(app.kt:7) at AppKt.main(app.kt:4) at AppKt.main(app.kt)
Но опять же поскольку генерируемое здесь исключение не обаботано, то программа при генерации исключения аварийно завершает работу. Чтобы этого не произошло, мы можем обработать генерируемое исключение:
fun main() { try { val checkedAge1 = checkAge(5) val checkedAge2 = checkAge(-115) } catch (e: Exception){ println(e.message) } } fun checkAge(age: Int): Int{ if(age < 1 || age > 110) throw Exception("Invalid value $age. Age must be greater than 0 and less than 110") println("Age $age is valid") return age }
Конструкция try может возвращать значение. Например:
fun main() { val checkedAge1 = try { checkAge(5) } catch (e: Exception) { null } val checkedAge2 = try { checkAge(-125) } catch (e: Exception) { null } println(checkedAge1) // 5 println(checkedAge2) // null } fun checkAge(age: Int): Int{ if(age < 1 || age > 110) throw Exception("Invalid value $age. Age must be greater than 0 and less than 110") println("Age $age is valid") return age }
В данном случае переменная checkedAge1 получает результат функцию checkAge()
. Если же произойдет исключение, тогда
переменная checkedAge1 получает то значение, которое указано в блоке catch, то есть в данном случае значение null.
При необрабходимости в блок catch можно добавить и другие выражения или возвратить другое значение:
fun main() { val checkedAge2 = try { checkAge(-125) } catch (e: Exception) { println(e.message); 18 } println(checkedAge2) } fun checkAge(age: Int): Int{ if(age < 1 || age > 110) throw Exception("Invalid value $age. Age must be greater than 0 and less than 110") println("Age $age is valid") return age }
В данном случае, если будет сгенерировано исключение, то конструкция try
выведет исключение и возвратит число 18. Возвращаемое значение
указывается после всех остальных инструкций в блоке catch.