Исключение, брошенное в улов и, наконец, пункт


155

На вопрос о Java в университете был приведен фрагмент кода:

class MyExc1 extends Exception {}
class MyExc2 extends Exception {}
class MyExc3 extends MyExc2 {}

public class C1 {
    public static void main(String[] args) throws Exception {
        try {
            System.out.print(1);
            q();
        }
        catch (Exception i) {
            throw new MyExc2();
        }
        finally {
            System.out.print(2);
            throw new MyExc1();
        }
    }

    static void q() throws Exception {
        try {
            throw new MyExc1();
        }
        catch (Exception y) {
        }
        finally {
            System.out.print(3);
            throw new Exception();
        }
    }
}

Меня попросили дать свой вывод. Я ответил13Exception in thread main MyExc2 , но правильный ответ 132Exception in thread main MyExc1. Почему это так? Я просто не могу понять, куда MyExc2идет.

Ответы:


167

Основываясь на прочтении вашего ответа и на том, как вы, вероятно, пришли к нему, я думаю, что вы думаете, что «выполняемое исключение» имеет «приоритет». Иметь ввиду:

Когда новое исключение выдается в блоке перехвата или блоке finally, который будет распространяться из этого блока, то текущее исключение будет отменено (и забыто), поскольку новое исключение распространяется наружу. Новое исключение начинает разматывать стек точно так же, как и любое другое исключение, прерываясь из текущего блока (блока catch или finally) и подвергаясь любым применимым блокам catch или finally по пути.

Обратите внимание, что применимые блоки catch или finally включают в себя:

Когда новое исключение выдается в блоке catch, новое исключение все еще подвергается блоку finally этого catch, если он есть.

Теперь проследите выполнение, помня, что всякий раз, когда вы нажимаете throw, вы должны прервать отслеживание текущего исключения и начать отслеживать новое исключение.


7
«Основываясь на прочтении вашего ответа и на том, как вы, вероятно, пришли к нему, я думаю, вы думаете, что« исключение в процессе »имеет« приоритет »» Спасибо ... это была именно моя мысль :)
Jubstuff

39

Вот что говорит Википедия об окончательном предложении:

Более распространенным является связанное предложение (наконец, или гарантировать), которое выполняется независимо от того, произошло исключение или нет, обычно для освобождения ресурсов, полученных в теле блока обработки исключений.

Давайте рассмотрим вашу программу.

try {
    System.out.print(1);
    q();
}

Итак, 1будет выводиться на экран, затем q()вызывается. В q(), исключение выдается. Затем исключение отлавливается, Exception yно ничего не делает. Наконец пункт затем выполняется (он должен), а значит, 3будет выведено на экран. Поскольку (в методе q()есть исключение, которое выдается в предложении finally , q()метод также передает исключение в родительский стек (согласно throws Exceptionобъявлению метода), new Exception()который будет выброшен и перехвачен catch ( Exception i ), MyExc2будет выдано исключение (сейчас добавьте его в стек исключений). ), но, наконец, в mainблоке будет выполняться первым.

Так что в

catch ( Exception i ) {
    throw( new MyExc2() );
} 
finally {
    System.out.print(2);
    throw( new MyExc1() );
}

, Наконец , статья называется ... (помните, мы просто поймали Exception iи бросили MyExc2) , в сущности, 2печатаются на экране ... и после того , как 2будет напечатан на экране, MyExc1исключение. MyExc1обрабатывается public static void main(...)методом.

Вывод:

"132Exception в теме потока MyExc1"

Лектор правильный! :-)

По сути , если у вас есть предложение finally в предложении try / catch, будет выполнено поле finally ( после перехвата исключения перед выбрасыванием перехваченного исключения)


catchВыполняется так q()кинул Exceptionиз своего finallyблока.
Петер Тёрёк

«В q () выбрасывается исключение, но перед тем, как исключение будет полностью выдано, сначала выполняется условие finally, поэтому на экран будет выведено 3.» Э-э ... нет, первое выброшенное исключение qпередает выполнение в пустой catchблок в q(который поглощает это исключение), затем в finallyблок в q. В конце концов, блок печатает 3, затем выдает новое исключение, которое благодаря qs throws Exceptionпередается по стеку родителю.
Powerlord

38

Исключения в блоке finally заменяют исключения в блоке catch.

Цитирование из издания 14 спецификации языка Java :

Если блок перехвата завершается преждевременно по причине R, то выполняется блок finally. Тогда есть выбор:

  • Если блок finally завершается нормально, то оператор try завершается преждевременно по причине R.

  • Если блок finally завершается преждевременно по причине S, тогда оператор try завершается преждевременно по причине S (и причина R отбрасывается).


21

Предложение finally выполняется, даже когда исключение генерируется из любого места в блоке try / catch.

Потому что это последнее, что будет выполнено в main и выдает исключение, это исключение, которое видят вызывающие.

Следовательно, важно убедиться, что в finallyпредложении ничего не выдается, поскольку оно может проглотить исключения из tryблока.


5
Он также будет выполнен ДАЖЕ, если в блоке try / catch нет исключений
nanda

2
+1: направить и в точку, не изгибая вниз весь стек, который ОП, кажется, уже понимает.
Powerlord

9

Не methodможет throwдва исключения одновременно. Он всегда будет бросать последний бросок exception, который в этом случае всегда будет одним из finallyблока.

Когда q()выбрасывается первое исключение из метода , оно будет перехвачено, а затем проглочено исключением, вызванным блоком finally.

q () -> Брошенный new Exception -> main catch Exception -> throw new Exception -> finally бросить новый exception(и тот, который из catch"потерян")


3

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

Exception currentException = null;

Поскольку каждое исключение выдается, "currentException" устанавливается в это исключение. Когда приложение заканчивается, если currentException равно! = Null, тогда среда выполнения сообщает об ошибке.

Кроме того, блоки finally всегда запускаются до выхода из метода. Затем вы можете запросить фрагмент кода для:

public class C1 {

    public static void main(String [] argv) throws Exception {
        try {
            System.out.print(1);
            q();

        }
        catch ( Exception i ) {
            // <-- currentException = Exception, as thrown by q()'s finally block
            throw( new MyExc2() ); // <-- currentException = MyExc2
        }
        finally {
             // <-- currentException = MyExc2, thrown from main()'s catch block
            System.out.print(2);
            throw( new MyExc1() ); // <-- currentException = MyExc1
        }

    }  // <-- At application exit, currentException = MyExc1, from main()'s finally block. Java now dumps that to the console.

    static void q() throws Exception {
        try {
            throw( new MyExc1() ); // <-- currentException = MyExc1
        }
        catch( Exception y ) {
           // <-- currentException = null, because the exception is caught and not rethrown
        }
        finally {
            System.out.print(3);
            throw( new Exception() ); // <-- currentException = Exception
        }
    }
}

Порядок, в котором выполняется приложение:

main()
{
  try
    q()
    {
      try
      catch
      finally
    }
  catch
  finally
}

1

Общеизвестно, что блок finally выполняется после try и catch и всегда выполняется .... Но, как вы видели, иногда немного сложно проверить этот фрагмент кода ниже, и вы увидите, что операторы return и throw не используют не всегда делать то, что они должны делать в том порядке, в котором мы ожидаем тему.

Приветствия.

/////////////Return dont always return///////

try{

    return "In Try";

}

finally{

    return "In Finally";

}

////////////////////////////////////////////


////////////////////////////////////////////    
while(true) { 

    try {

        return "In try";

   } 

   finally{

        break;     

    }          
}              
return "Out of try";      
///////////////////////////////////////////


///////////////////////////////////////////////////

while (true) {     

    try {            

        return "In try";    

     } 
     finally {   

         continue;  

     }                         
}
//////////////////////////////////////////////////

/////////////////Throw dont always throw/////////

try {

    throw new RuntimeException();

} 
finally {

    return "Ouuuups no throw!";

}
////////////////////////////////////////////////// 

1
class MyExc1 extends Exception {}
class MyExc2 extends Exception {}
class MyExc3 extends MyExc2 {}

public class C1 {
    public static void main(String[] args) throws Exception {
        try {
            System.out.print("TryA L1\n");
            q();
            System.out.print("TryB L1\n");
        }
        catch (Exception i) {
            System.out.print("Catch L1\n");                
        }
        finally {
            System.out.print("Finally L1\n");
            throw new MyExc1();
        }
    }

    static void q() throws Exception {
        try {
            System.out.print("TryA L2\n");
            q2();
            System.out.print("TryB L2\n");
        }
        catch (Exception y) {
            System.out.print("Catch L2\n");
            throw new MyExc2();  
        }
        finally {
            System.out.print("Finally L2\n");
            throw new Exception();
        }
    }

    static void q2() throws Exception {
        throw new MyExc1();
    }
}

Порядок:

TryA L1
TryA L2
Catch L2
Finally L2
Catch L1
Finally L1        
Exception in thread "main" MyExc1 at C1.main(C1.java:30)

https://www.compilejava.net/


1
Хотя этот фрагмент кода может быть решением, включение пояснения действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин, по которым вы предлагаете свой код
Рахул Гупта,

1

Логика ясна до завершения печати 13. Тогда исключение брошено в q()поймана catch (Exception i)в main()и new MyEx2()готов бросить. Однако, прежде чем finallyвызвать исключение, блок должен быть выполнен первым. Затем вывод становится 132и finallyпросит выдать еще одно исключение new MyEx1().

Поскольку метод не может выдать больше одного Exception, он всегда выбрасывает последнее Exception. Другими словами, если оба блока catchи finallyблок пытаются выполнить бросок Exception, то Exceptionвходящие данные будут проглочены, и будет выдано только исключение finally.

Таким образом, в этой программе исключение MyEx2проглатывается и MyEx1выбрасывается. Это исключение выбрасывается main()и больше не перехватывается, поэтому JVM останавливается и конечный результат - 132Exception in thread main MyExc1.

По сути, если у вас есть предложение finallyin try/catch, a finallyбудет выполняться ПОСЛЕ перехвата исключения , но ДО создания любого перехваченного исключения , и ТОЛЬКО самое последнее исключение будет выброшено в конце .


0

Я думаю, вам просто нужно пройтись по finallyблокам:

  1. Выведите «1».
  2. finallyв qпечати "3".
  3. finallyв mainпечати "2".

0

Чтобы справиться с такой ситуацией, то есть обработать исключение, вызванное блоком finally. Вы можете окружить блок finally блоком try: Посмотрите на приведенный ниже пример в python:

try:
   fh = open("testfile", "w")
   try:
      fh.write("This is my test file for exception handling!!")
   finally:
      print "Going to close the file"
      fh.close()
except IOError:
   print "Error: can\'t find file or read data"

-1

Я думаю, что это решит проблему:

boolean allOk = false;
try{
  q();
  allOk = true;
} finally {
  try {
     is.close();
  } catch (Exception e) {
     if(allOk) {
       throw new SomeException(e);
     }
  }
}

3
Какую проблему вы собираетесь «решить»? Вы имеете в виду вопрос на экзамене? ну это уже ответили. Если вы имеете в виду проблему данного кода, так как это всего лишь экзаменационный вопрос, то нет смысла винить его.
Earth Engine
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.