Виняток кинуто у вилов та нарешті пункт


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

Виходячи з того, щоб прочитати вашу відповідь і побачити, як ви, ймовірно, придумали її, я вважаю, що ви вважаєте, що "незавершений виняток" має "перевагу". Пам'ятай:

Коли новий виняток буде кинутий у блок спіймання або, нарешті, блок, який поширюватиметься з цього блоку, то поточний виняток буде скасовано (і забуто), оскільки новий виняток поширюється назовні. Новий виняток починає розмотувати стек так само, як і будь-який інший виняток, припиняючи роботу з поточного блоку (лову або, нарешті, блоку) і підлягає будь-якому застосовному лову або, нарешті, блокує на цьому шляху.

Зауважте, що застосований улов або, нарешті, блоки включає:

Коли новий виняток буде кинуто в блок ловлі, новий виняток все ще підлягає остаточному блоку цього улову, якщо він є.

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


7
«Виходячи з того, щоб прочитати вашу відповідь і побачити, як ви, ймовірно, придумали її, я вважаю, ви думаєте, що" незавершений виняток "має" перевагу "» Дякую ... це була саме моя думка :)
Jubstuff

39

Ось що говорить Вікіпедія про остаточний пункт:

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

Давайте розберемо вашу програму.

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

Отже, 1буде виводитися на екран, потім q()називається. У q(), викинуто виняток. Тоді виняток виловлює, Exception yале він нічого не робить. Нарешті пункт потім виконується (він повинен), а значить, 3буде виведено на екран. Тому що (у методі q()є виняток, викинутий в остаточному пункті, також 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"

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

По суті , якщо у вас остаточно в пункті пробування / лову, остаточно буде виконано ( після вилучення винятку перед викиданням спійманого винятку)


catchВиконується так q()кинув Exceptionзі свого finallyблоку.
Péter Török

«В д (), генерується виняток , але до того , як виняток повністю викинутий, фінально пункт спочатку виконується, тому, 3 буде виведено на екран.» Е - е ... ні, перше виключення кинуто в qвиконання переходить до порожній catchблок в q(який проковтує цей виняток), потім у finallyблок в q. Саїд нарешті блокує відбитки 3, а потім викидає новий виняток, який завдяки qs throws Exceptionпередається до стека батькові.
Powerlord

38

Винятки в остаточному блоці витісняють винятки в блоці вилову.

Цитуючи видання Java специфікації 14 :

Якщо блок лову різко завершується з причини R, тоді остаточно блок виконується. Тоді є вибір:

  • Якщо блок нарешті завершується нормально, то оператор try різко завершується з причини R.

  • Якщо остаточний блок різко завершується з причини S, то оператор спробу завершується різко з причини S (а причина R відкидається).


21

Зрештою, стаття виконується навіть тоді, коли викид викидається з будь-якого місця в блоці спробу / лову.

Оскільки це останнє, що виконується у програмі, mainі він викидає виняток, це виняток, який бачать абоненти.

Звідси важливо переконатися, що finallyстаття нічого не кидає, оскільки вона може проковтнути винятки з tryблоку.


5
Він також буде виконаний ВСІМ, якщо не буде виключення, кинутого в блок "try / catch"
nanda

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

9

Не methodможе бути throwдва винятки одночасно. Він завжди буде кидати останній кинутий exception, який у цьому випадку завжди буде той, хто з finallyблоку.

Коли q()буде викинуто перше виняток із методу , він буде вилучений, а потім проковтнуто винятковим блоком, який остаточно блокується.

q () -> кинутий new Exception -> main catch Exception -> throw new Exception -> finally кинути нове exception(і той, що з catch"загублений")


3

Найпростіший спосіб подумати про це - уявити, що існує ціла змінна глобальна для всієї програми, яка містить поточний виняток.

Exception currentException = null;

Коли кожен виняток кидається, "currentException" встановлюється для цього винятку. Коли додаток закінчується, якщо currentException! = Null, час виконання повідомляє про помилку.

Також, нарешті, блоки завжди виконуються до виходу методу. Потім ви можете зажадати фрагмент коду для:

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

Добре відомо, що остаточний блок виконується після спроби лову і завжди виконується .... Але, як ви бачили, це трохи хитро, іноді перевіряйте ці фрагменти коду нижче, і ви будете, що заяви про повернення та кидання не Завжди робити те, що вони повинні робити в тому порядку, на який ми очікуємо тему.

Ура.

/////////////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()і A new MyEx2()готовий до кидка. Однак перед тим, як викинути виняток, finallyблок потрібно виконати спочатку. Тоді вихід стає 132і finallyпросить викинути ще один виняток new MyEx1().

Оскільки метод не може кинути більше одного Exception, він завжди буде викинути останні Exception. Іншими словами, якщо обидва catchі finallyблоки намагаються кинути Exception, то Exceptionулов заковтується і буде виключено лише виняток у finally.

Таким чином, у цій програмі Виняток MyEx2проковтується і MyEx1кидається. Цей виняток викидається main()і більше не виловлюється, тому JVM зупиняється і кінцевий вихід є 132Exception in thread main MyExc1.

По суті, якщо у вас є finallyв try/catchреченні, finallyбуде виконуватися ПІСЛЯ вилов виключення , але , перш ніж викинути будь спіймана виняток , і ТІЛЬКИ Lastest виняток буде кинуто в кінці кінців .


0

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

  1. Друк "1".
  2. finallyу qдруку "3".
  3. finallyу mainдруку "2".

0

Для вирішення подібних ситуацій, тобто обробку виключення, піднятого остаточно блоком. Ви можете оточити остаточний блок, спробуйте блок: Подивіться на приклад нижче в 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
Яку проблему ви збираєтеся "вирішити"? Ви маєте на увазі питання на іспиті? ну це вже відповіло. Якщо ви маєте на увазі проблему даного коду, оскільки це лише екзаменаційне питання, винуватити його немає сенсу.
Земляний двигун
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.