Почему в данном примере было потеряно необработанное исключение:
class MyClass {
int i;
String getString(){
try {
i = i / 0;
return "try";
}finally {
return "finally";
}
}
}
public class Test {
public static void main(String[] args) {
MyClass myClass = new MyClass();
String s = myClass.getString();
System.out.println(s);
}
}
В блоке try происходит деление на 0, что должно породить: Exception in thread "main" java.lang.ArithmeticException: / by zero
, однако такого не происходит и метод возвращается строку finally
.
Как уже заметили в других ответах и комментариях, блок finally
выполняется всегда. Можно удовлетвориться объяснением, что finally
это тоже обработчик и это он проглатывает исключение. Но если вы вместо return "finally"
напишите System.out.println("finally")
то исключение всё-таки будет проброшено вызывающему методу. Чтобы разобраться почему так, нужно посмотреть, как обрабатываются исключения на уровне байткода.
Скомпилируем такой код
public class Example {
private static String test() {
int i;
try {
i = 1 / 0;
} catch (IllegalStateException exc) { // Произвольные исключения для примера
i = 2;
} catch (IllegalArgumentException exc) {
i = 3;
} finally {
i = 42;
}
return String.valueOf(i);
}
public static void main(String[] args) {
test();
}
}
И посмотрим, что из него получилось:
private static java.lang.String test();
descriptor: ()Ljava/lang/String;
flags: ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=3, args_size=0
0: iconst_1 --
1: iconst_0 |_ Блок try
2: idiv |
3: istore_0 --
4: bipush 42 --
6: istore_0 |- Блок finally
7: goto 34 --
10: astore_1 --
11: iconst_2 |- Блок catch для IllegalStateException
12: istore_0 --
13: bipush 42 --
15: istore_0 |- Ещё один finally?
16: goto 34 --
19: astore_1 --
20: iconst_3 |- Блок catch для IllegalArgumentException
21: istore_0 --
22: bipush 42 --
24: istore_0 |- Опять finally?!
25: goto 34 --
28: astore_2 --
29: bipush 42 |
31: istore_0 |- И ещё один, но длинный!
32: aload_2 |
33: athrow --
34: iload_0
35: invokestatic #4 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
38: areturn
Exception table:
from to target type
0 4 10 Class java/lang/IllegalStateException
0 4 19 Class java/lang/IllegalArgumentException
0 4 28 any
10 13 28 any
19 22 28 any
Компилятор сгенерировал блок finally
для успешного завершения блока try
, для каждого блока catch
и ещё один на тот случай, если исключение не было перехвачено. Причём последний начинается опкодом astore_2
, а заканчивается опкодами - aload_2
и athrow
. Первый опкод сохраняет исключение из стека в переменную. Второй загружает его обратно после выполнения тела блока. А последний выбрасывает сохранённое исключение. То есть в норме, если исключение не было обработано, то должно быть выброшено повторно в конце блока finally
. Но если добавить в блок finally
инструкцию return
, то все блоки становятся одинаковыми:
bipush 42
istore_0
ldc #3 // String "finally"
areturn
Больше нет инструкции повторного выброса исключения. Получается, что исключение теряется из-за инструкции return
.
В блоке try происходит деление на 0, что должно породить:Exception in thread "main" java.lang.ArithmeticException: / by zero
Оно есть. Но на то и try-catch-finally
блок, чтобы обрабатывать исключения. Т.к. блок catch
отсутствует, то ничего и не делается (на самом деле делается, но остается внутри класса MyClass и наружу не отдается).
Если вынести функцию getString
в main()
класса Test
, то увидите, что исключение есть и информация о нем выводится, а потом печатается "finally
".
У меня все работает:
"finally"
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Main.main(Main.java:12)
Сначала происходит исключение, try
его принимает, т.к. у вас нет блока catch
, то оно не обрабатывается(а выходит в виде вывода выше). После этого программа переходит к выполнению блока finally
. Блок finally
выполняется всегда, независимо от того, было ли исключение, есть ли блок catch
, и т.п.
Если бы у вас был такой код:
try {
i = i / 0;
System.out.println("try");
} catch(Exception ex) {
System.out.println("catch");
} finally {
System.out.println("finally);
}
То был бы вывод:
catch
finally
Хороший материал для новичков по исключениям.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Если поменять на AppCompatActivity, то ютуб ругаетсяЧто делать?
При выполнении нативного запроса в базу данных сервер падает с ошибкой: