java потеря необработанного исключения

116
26 октября 2019, 06:00

Почему в данном примере было потеряно необработанное исключение:

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.

Answer 1

Как уже заметили в других ответах и комментариях, блок 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.

Answer 2

В блоке try происходит деление на 0, что должно породить:Exception in thread "main" java.lang.ArithmeticException: / by zero

Оно есть. Но на то и try-catch-finally блок, чтобы обрабатывать исключения. Т.к. блок catch отсутствует, то ничего и не делается (на самом деле делается, но остается внутри класса MyClass и наружу не отдается).

Если вынести функцию getString в main() класса Test, то увидите, что исключение есть и информация о нем выводится, а потом печатается "finally".

Answer 3

У меня все работает:

"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

Хороший материал для новичков по исключениям.

READ ALSO
Из-за YoutubeActivity не могу использовать метод getSupportActionBar().setDisplayShowHomeEnabled(true);

Из-за YoutubeActivity не могу использовать метод getSupportActionBar().setDisplayShowHomeEnabled(true);

Если поменять на AppCompatActivity, то ютуб ругаетсяЧто делать?

137
Union в postgresql (JPA)

Union в postgresql (JPA)

При выполнении нативного запроса в базу данных сервер падает с ошибкой:

217
Объясните критерии ТЗ

Объясните критерии ТЗ

Пишу ТЗНебольшая иерархия классов с реализацией основных принципов ООП

136