W pytaniu do Javy na uniwersytecie był taki fragment kodu:
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();
}
}
}
Poproszono mnie o przedstawienie wyników. Odpowiedziałem 13Exception in thread main MyExc2
, ale prawidłowa odpowiedź brzmi 132Exception in thread main MyExc1
. Dlaczego tak jest? Po prostu nie mogę zrozumieć, dokąd MyExc2
zmierza.
Oto, o czym Wikipedia mówi o klauzuli końcowej:
Przeanalizujmy Twój program.
Tak,
1
zostanie wyświetlona na ekranie, a następnieq()
zostanie wywołana. W programieq()
jest zgłaszany wyjątek. Wyjątek zostaje wtedy złapany,Exception y
ale nic nie robi. Końcu punkt jest następnie wykonywane (musi), więc3
będą wyświetlane na ekranie. Ponieważ (w sposóbq()
nie wyjątkiem wyrzucane w końcu klauzuli równieżq()
sposób przechodzi wyjątku stosu macierzystego (Nawiasemthrows Exception
w zgłoszeniu metoda)new Exception()
zostanie wyrzucony i złapaćcatch ( Exception i )
,MyExc2
zostanie wyrzucony wyjątek (na razie dodać go do stosu wyjątku ), ale ostatni wmain
bloku zostanie wykonany jako pierwszy.Więc w
Wreszcie klauzula nazywa ... (pamiętam, właśnie złapany
Exception i
i wyrzuconyMyExc2
), w istocie,2
jest drukowany na ekranie ... i po2
wydrukowany na ekran,MyExc1
jest wyjątek.MyExc1
jest obsługiwany przezpublic static void main(...)
metodę.Wynik:
Wykładowca ma rację! :-)
Zasadniczo , jeśli masz klauzulę final in try / catch, zostanie wykonana funkcja final ( po przechwyceniu wyjątku przed wyrzuceniem przechwyconego wyjątku)
źródło
catch
Jest wykonywany odq()
rzuciłException
z własnegofinally
bloku.q
przekazuje wykonanie do pustycatch
blok wq
(który połyka ten wyjątek), a następnie dofinally
blokuq
. Wreszcie wymieniony blok drukuje3
, a następnie generuje nowy wyjątek, które dziękiq
jestthrows Exception
przekazywany na stos do nadrzędnego.Wyjątki w bloku końcowym zastępują wyjątki w bloku catch.
Cytat z wydania Java Language Specification 14 :
źródło
Ostateczna klauzula jest wykonywana nawet wtedy, gdy wyjątek jest zgłaszany z dowolnego miejsca w bloku try / catch.
Ponieważ jest to ostatni do wykonania w
main
i zgłasza wyjątek, jest to wyjątek, który widzą wywołujący.Stąd tak ważne jest upewnienie się, że
finally
klauzula niczego nie wyrzuca, ponieważ może połknąć wyjątki ztry
bloku.źródło
Nie
method
może miećthrow
dwóch wyjątków w tym samym czasie. Zawsze wyrzuci ostatni rzuconyexception
, który w tym przypadku będzie zawsze tym zfinally
bloku.Kiedy pierwszy wyjątek od metody
q()
zostanie wyrzucony, zostanie złapany, a następnie połknięty przez ostatni wyrzucony wyjątek.q () -> thrown
new Exception
->main
catch Exception
->throw
new Exception
->finally
wrzuć nowyexception
(a ten z tegocatch
jest „zagubiony”)źródło
Najłatwiej o tym pomyśleć, wyobrażając sobie, że istnieje zmienna globalna dla całej aplikacji, która przechowuje bieżący wyjątek.
Gdy zgłaszany jest każdy wyjątek, „currentException” jest ustawiany na ten wyjątek. Gdy aplikacja kończy się, jeśli currentException ma wartość! = Null, środowisko wykonawcze zgłasza błąd.
Ponadto ostatnie bloki są zawsze uruchamiane przed zakończeniem metody. Następnie możesz zażądać fragmentu kodu, aby:
Kolejność wykonywania aplikacji to:
źródło
Powszechnie wiadomo, że ostatni blok jest wykonywany po wykonaniu try and catch i zawsze jest wykonywany .... Ale jak widzieliście, czasami jest to trochę skomplikowane, sprawdzajcie ten fragment kodu poniżej i okaże się, że instrukcje return i throw nie nie zawsze robią to, co powinni robić w kolejności, w jakiej oczekujemy od tematu.
Twoje zdrowie.
źródło
Zamówienie:
https://www.compilejava.net/
źródło
Logika jest jasna aż do zakończenia drukowania
13
. Wtedy wyjątek wrzuconyq()
zostaje złapany przezcatch (Exception i)
sięmain()
inew MyEx2()
jest gotowy do rzucania. Jednak przed wyrzuceniem wyjątkufinally
należy najpierw wykonać blok. Następnie wyjście staje się132
ifinally
prosi o zgłoszenie kolejnego wyjątkunew MyEx1()
.Ponieważ metoda nie może wyrzucić więcej niż jednej
Exception
, zawsze będzie rzucać ostatniąException
. Innymi słowy, jeśli obacatch
ifinally
bloki spróbują rzucićException
, połkniętyException
zostanie złapany i tylko wyjątekfinally
zostanie wyrzucony .Tak więc w tym programie Wyjątek
MyEx2
jest połykany iMyEx1
wyrzucany. Ten wyjątek jest wyrzucanymain()
i nie jest już przechwytywany, dlatego JVM zatrzymuje się, a ostateczny wynik jest132Exception in thread main MyExc1
.W istocie, jeśli masz
finally
wtry/catch
klauzuli, afinally
zostanie wykonane PO złapaniu wyjątku , ale PRZED wyrzuceniem dowolnego przechwyconego wyjątku , a na końcu zostanie wyrzucony TYLKO ostatni wyjątek .źródło
Myślę, że po prostu musisz przejść
finally
bloki:finally
wq
druku „3”.finally
wmain
druku „2”.źródło
Aby poradzić sobie z tego rodzaju sytuacjami, tj. Obsłużyć wyjątek wywołany przez blok final. Możesz otoczyć ostatni blok blokiem try: Spójrz na poniższy przykład w pythonie:
źródło
Myślę, że to rozwiązuje problem:
źródło