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

Рассмотрим метод 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) — это список методов, которые были вызваны, имена файлов и номера строк, в которых это произошло.

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 будет проигнорирован. Как только выбрасывается исключение подходящего класса (в данном случае — NumberFormatException), управление передаётся блоку catch. Если выброшено исключение неподходящего класса, оно не попадает в catch.

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

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

int[] nullArray = null;
int nullArrayLength = nullArray.length; // NullPointerException
int firstElementOfNullArray = nullArray[0]; // NullPointerException
nullArray[0] = 100500; // NullPointerException

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 недоступен. ¯\_(ツ)_/¯
Комментарии к уроку

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