Javanese Online

Обработка исключений. Проверяемые и непроверяемые исключения

Рассмотрим метод Integer.parseInt(String), который принимает число в форме строки (например, "94", "865") и возвращает его целочисленное представление (например, 94, 865).

Очевидно, что Integer.parseInt("650") == 650, а Integer.parseInt("-1") == -1. Но чему равно выражение Integer.parseInt("surprise")? Нулю? Это было бы ужасно, такую ошибку невозможно было бы отловить. Сумме числовых значений всех букв? Ещё хуже.

Метод parseInt не может вернуть числовое значение строки "surprise", в десятичной системе есть только цифры 0..9, эта задача невыполнима. Метод выбрасывает исключение.

{"name":"main","subCalls":[{"name":"parseInt(\"surprise\")","subCalls":[{"name":"parseInt(\"surprise\", 10)","subCalls":[{"name":"NumberFormatException.forInputString(\"surprise\")","subCalls":[]},{"type":"crash","name":"throw","subCalls":[]}]}]}]}

Выброс исключения прерывает выполнение метода, если его никто не ловит. В примере оно выбрасывается из метода parseInt(String s, int radix), затем прерывает метод parseInt(String s), затем — main. В консоль будет выведена трассировка стека вызовов (stack trace) — это список методов, которые были вызваны, c именами файлов и номерами строк, в которых это произошло.

Exception in thread "main" java.lang.NumberFormatException: For input string: "surprise"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:580)
	at java.lang.Integer.parseInt(Integer.java:615)
	at online.javanese.basics.oop.exceptions.ParseInt.main(ParseInt.java:6)

Если в данном контексте вы не можете ничего сделать с исключением — не ловите его, пусть программа падает. Если же вы можете как-то восстановиться после исключения, можно его поймать:

package online.javanese.basics.oop.exceptions;

public class ParseInt {

    public static void main(String[] args) {
        try {
            Integer.parseInt("surprise");
        } catch (NumberFormatException e) {
            System.out.println("Так себе число.");
        }
    }
}

В блоке try выполняются действия, которые могут привести к исключению. Если исключение не будет выброшено, блок catch будет проигнорирован. Если же любой код, выполняющийся внутри try, выбрасывает исключение подходящего типа (в данном случае — NumberFormatException), управление передаётся блоку catch. Если выброшено исключение неподходящего класса, оно не попадает в catch.

Непроверяемые исключения

Иерархия исключений, наследующих RuntimeException, предполагает, что ошибка в нахдится коде, т. е. в ней виновен программист. Следовательно, восстановить выполнение программы не удастся, нужно исправлять ошибку в коде, поэтому и ловить такие исключения не нужно. Вот основные представители:

int[] zeroSizeArray = new int[0];
int firstItem = zeroSizeArray[0]; // ArrayIndexOutOfBoundsException
zeroSizeArray[0] = 200700; // ArrayIndexOutOfBoundsException

int lol = 10 / 0; // ArithmeticException

int numberFromString = Integer.parseInt("not a number"); // NumberFormatException

Проверяемые исключения

Проверяемые исключения (checked exceptions) — такие исключения, которые необходимо ловить. Их возникновение — это штатная ситуация. Например, посчитаем отпечаток пароля с помощью алгоритма SHA-512 и выведем его как массив байт. Это удастся сделать, только если на данном компьютере доступен этот алгоритм.

План А: перебрасываем

try {
    MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
    System.out.println(Arrays.toString(sha512.digest("password".getBytes())));
} catch (NoSuchAlgorithmException e) {
    // если SHA-512 не поддерживается,
    // выполнить задачу невозможно — падаем,
    // бросая непроверяемое исключение,
    // в качестве причины которого указываем пойманное:
    throw new RuntimeException(e);
}

Успех:

[-79, 9, -13, -69, -68, 36, 78, -72, 36, 65, -111, 126, -48, 109, 97, -117, -112, 8, -35, 9, -77, -66, -3, 27, 94, 7, 57, 76, 112, 106, -117, -71, -128, -79, -41, 120, 94, 89, 118, -20, 4, -101, 70, -33, 95, 19, 38, -81, 90, 46, -90, -47, 3, -3, 7, -55, 83, -123, -1, -85, 12, -84, -68, -122]

Неудача, алгоритм недоступен:

Exception in thread "main" java.lang.RuntimeException: java.security.NoSuchAlgorithmException: SHA-512 MessageDigest not available
	at online.javanese.basics.oop.exceptions.Sha512.main(Sha512.java:14)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.security.NoSuchAlgorithmException: SHA-512 MessageDigest not available
	at sun.security.jca.GetInstance.getInstance(GetInstance.java:159)
	at java.security.Security.getImpl(Security.java:695)
	at java.security.MessageDigest.getInstance(MessageDigest.java:167)
	at online.javanese.basics.oop.exceptions.Sha512.main(Sha512.java:11)
	... 5 more

План Б: восстанавливаемся

try {
    MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
    System.out.println(Arrays.toString(sha512.digest("password".getBytes())));
} catch (NoSuchAlgorithmException e) {
    System.out.println("SHA-512 недоступен. ¯\\_(ツ)_/¯");
}

Успех выглядит так же. Неудача:

SHA-512 недоступен. ¯\_(ツ)_/¯
Комментарии
{"type":"lessonComments","id":"6ba10846-1323-4146-901e-61b5dcf0a4ea","comments":[]}

Сообщить об ошибке

Javanese.Online в GitHub

Чаты и каналы в Telegram

RSS-лента