Wykład 8: Obsługa Wyjątków
Transkrypt
Wykład 8: Obsługa Wyjątków
Wykład 8: Obsługa Wyjątków Wyjątki Wyjątek to sytuacja nienormalna, która pojawia się w trakcie wykonania programu. W językach bez obsługi wyjątków, błędy są wykrywane i obsługiwane ręcznie, zwykle przez kody błędów. Java dostarcza specjalnych konstrukcji językowych do sygnalizacji, wykrywania i obsługi błędów. Wyjątki w Javie Wyjątek to obiekt, który opisuje sytuację wyjątkową (błędną) powstałą w kodzie programu: ● ● ● Kiedy powstaje błąd, wyjątek opisujący go jest "wyrzucany" w metodzie która ten błąd spowodowała. Metoda może "wyłapać" i "obsłużyć" wyjątek samodzielnie, lub przekazać go dalej. Błąd jest na koniec wyłapany i obsługiwany. Konstrukcje Obsługi Wyjątków try – otacza część programu, którą chcemy monitorować na wypadek sygnalizacji błędów ● catch – w parze z try, wyłapuje określone wyjątki i obsługuje je w pewien sposób ● throw – sygnalizuje powstanie określonego wyjątku ● throws – określenie jakie wyjątki może dana metoda sygnalizować ● finally – kod, który musi być koniecznie wywołany przed opuszczeniem danej metody ● Blok Obsługi Wyjątków Blok try/catch/finally do obsługi dwóch rodzajów wyjątków (TypWyjatku1 i TypWyjatku2): try { //monitorowana część kodu } catch(TypWyjatku1 e) { //obsluga wyjatku dla typu 1 } catch(TypWyjatku2 e) { //obsluga wyjatku dla typu 2 } finally { //kod do wykonanie przed zakonczeniem } Hierarchia Wyjątków ● Throwable – obejmuje wszystkie wyjątki ● Exception – wyjątki do wyłapania przez programy użytkowe ● RuntimeException – definiowane automatycznie dla programów: ● dzielenie przez zero ● indeksowanie tablic ● itp. ● TypWyjatku – wyjątki użytkownika ● Error – nie do wyłapania przez programy użytkowe, błędy środowiska wykonawczego Domyślna Obsługa Wyjątków class Wyjatek0 { public static void main(String args[]) { int d = 0; int a = 42 / d; } } Gdy system wykrywa dzielenie przez zero, tworzy nowy obiekt wyjątku, i wyrzuca go. Z braku własnej procedury obsługi, wyjątek jest przechwycony przez procedurę domyślną, która wyświetla komunikat, stos wywołań i powoduje zakończenie. Domyślna Obsługa Wyjątków Stos wywołań: ciąg wywołań metod które prowadziły do wystąpienia błędu. class Wyjatek1 { static void metoda() { int d = 0; int a = 10 / d; } public static void main(String args[]) { Wyjatek1.metoda(); } } Własna Obsługa Wyjątków Korzyści własnej obsługi błędów: umożliwia poprawianie błędów, zapobiega zakończeniu działania programu. class Wyjatek2 { public static void main(String args[]) { int d, a; Należy umieścić kod do monitorowania w bloku try: try { d = 0; a = 42 / d; System.out.println("Nieosiągalne"); } Własna Obsługa Wyjątków Należy określić które wyjątki chcemy wyłapać: catch (ArithmeticException e) { Oraz jak te wyjątki obsłużyć: } System.out.println("Dzielenie przez 0"); Kod po obsłudze wyjątku: } } System.out.println("Po wyjatku"); Try i Catch Bloki try i catch stanowią parę. Gdy wyrzucany jest wyjątek w bloku try: try { d = 0; a = 42 / d; System.out.println("Nieosiągalne"); kontrola przechodzi natychmiast do bloku catch: } catch (ArithmeticException e) { ... } catch nie może wyłapać wyjątków z innych bloków try, z wyłączeniem zagnieżdżonych bloków try. Maskowanie Błędów Wyjątek jest obsługiwany, potem program kontynuuje tak jakby nic się nie wydarzyło. Generowanie liczb losowych: import java.util.Random; class ObslugaBledow { public static void main(String args[]) { int a = 0, b = 0, c = 0; Random r = new Random(); Maskowanie Błędów Obsługa błędu gdy jedna z pary liczb jest zerem: } } for (int i=0; i<32000; i++) { try { b = r.nextInt(); c = r.nextInt(); a = 12345 / (b / c); } catch (ArithmeticException e) { System.out.println("Dzielenie przez 0"); a = 0; } System.out.println("i: " + i + "a: " + a); } Wyświetlanie Opisu Wyjątku Klasa Throwable przesłania metodę toString() tak by wyświetlała opis wyjątku. try { ... } catch (ArithmeticException e) { System.out.println("Wyjatek: " + e); a = 0; } Obsługa Kilku Wyjątków Na Raz Jeden blok try i kilka catch dla różnych wyjątków. class WieleBledow { public static void main(String args[]) { try { int a = args.length; System.out.println("a= " + a); Ryzyko dzielenia przez zero: int b = 42 / a; Indeks poza zakresem tablicy: int c[] = { 1 }; c[42] = 99; Obsługa Kilku Wyjątków Na Raz Wyłapanie i obsługa błędu dzielenia przez zero: } catch(ArithmeticException e) { System.out.println("Dzielenie przez 0"); Wyłapanie i obsługa błędu indeksowania poza tablicą: } catch(ArrayIndexOutOfBoundsException e) { System.out.println("Index poza tablica"); } Kod po obsłudze błędów: } System.out.println("Po obsludze bledow"); } Kolejność Obsługi Wyjątków W sekwencji catch, wyjątki pod-klasy muszą wystąpić przed wyjątkami nad-klasy. Pojawi się błąd kompilacji: class WyjatekPodKlasa { public static void main(String args[]) { try { int a = 0; int b = 42 / a; } catch(Exception e) { System.out.println("Wyjatek generyczny"); } catch(ArithmeticException e) { System.out.println("Nieosiagalny"); } } } Zagnieżdżony Blok try Jeden blok try wewnątrz innego try. class ZagniezdzonyTry { public static void main(String args[]) { Zewnętrzny blok try: try { int a = args.length; Ryzyko dzielenia przez zero: int b = 42 / a; System.out.println("a= " + a); Zagnieżdżony Blok try Wewnętrzny blok try: try { Ryzyko dzielenia przez zero: if (a == 1) a = a /(a-a); Wykroczenie poza zakres tablicy: if (a == 2) { int c[] = {1}; c[42] = 99; } Zagnieżdżony Blok try Obsługa wyjątku wykroczenia poza zakres tablicy powstałego w bloku wewnętrznym try: } catch(ArrayIndexOutOfBoundsException e){ System.out.println("Index za tablica"); } Obsługa wyjątku błędu dzielenia przez zero powstałego w bloku zewnętrznym try: } } } catch (ArithmeticException e) { System.out.println("Dzielenie przez 0); } Zagnieżdżony Blok try wyjątek powstaje w bloku wewnętrznym try i jest obsłużony przez catch w tym samym bloku ● wyjątek powstaje w bloku wewnętrznym try i jest obsłużony przez catch w bloku zewnętrznym ● wyjątek powstaje w bloku zewnętrznym try i jest obsłużony przez catch w bloku zewnętrznym ● brakuje instrukcji catch w obu blokach - wyjątek obsługiwany jest przez środowisko wykonawcze ● Metody i Występowanie Bloków try class MetodaZagniezdzonyTry { Metoda z wewnętrznym blokiem try: static void zagniezdzonyTry(int a) { try { if (a == 1) a = a /(a-a); if (a == 2) { int c[] = {1}; c[42] = 99; } } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Index poza tablica); } } Metody i Występowanie Bloków try Metoda z zewnętrznym blokiem try: } public static void main(String args[]) { try { int a = args.length; int b = 42 / a; System.out.println("a= " + a); zagniezdzonyTry(a); } catch (ArithmeticException e) { System.out.println("Dzielenie przez 0); } } Wyrzucanie Wyjątków Program użytkownika może sam wyrzucać wyjątki: throw object; Obiekt musi być klasy Throwable, albo jej pod-klasy. Uzyskanie obiektu klasy Throwable: ● użycie operatora new ● użycie parametru w instrukcji catch Obsługa throw Przerwanie wykonania na instrukcji throw: czy najbliższy otaczający blok try posiada instrukcję catch dla obsługi danego wyjątku? ● w przeciwnym razie, czy kolejny otaczający blok try posiada tą instrukcję? ● w przeciwnym razie, wykonaj domyślną obsługę wyjątku: przerwanie wykonania, drukowanie stosu ● Demonstracja throw class ThrowDemo { static void metoda() { try { Tworzy nowy wyjątek i go wyrzuca: throw new NullPointerException("demo"); Wyjątek jest wyłapany natychmiast: } catch(NullPointerException e) { System.out.println("Zlapany w metodzie"); Demonstracja throw Obsługa polega na przesłaniu wyjątku dalej: } } throw e; Wyjątek jest złapany ponownie przez metodę main: } public static void main(String args[]) { try { metoda(); } catch(NullPointerException e) { System.out.println("Zlapany ponownie"); } } Tworzenie Standardowych Wyjątków Tworzenie obiektu standardowej klasy wyjątku: throw new NullPointerException("demo"); Wszystkie standardowe wyjątki mają dwa konstruktory: bezparametrowy ● z parametrem String opisującym wyjątek; dostępny przez getMessage() w Throwable ● Deklaracja throws Jeśli metoda może spowodować wyjątek którego sama nie jest w stanie obsłużyć, to musi ten fakt opisać. typ nazwa(parametry) throws wyjatki { ... } Niezbędne jest wymienienie wszystkich wyjątków, oprócz typów Error i RuntimeException. Brak Deklaracji throws Metoda wyrzuca wyjątek którego ani nie wyłapuje, ani nie deklaruje. Ten program nie kompiluje się. class ThrowsDemo1 { static void metoda() { System.out.println("Wewnatrz metody"); throw new IllegalAccessException("demo"); } public static void main(String args[]) { metoda(); } } Dodana Deklaracja throws Jedna metoda deklaruje, druga wyłapuje wyjątek: class ThrowsDemo2 { static void metoda() throws IllegalAccessException { System.out.println("Wewnatrz metody"); throw new IllegalAccessException("demo"); } public static void main(String args[]) { try { metoda(); } catch (IllegalAccessException e) { System.out.println("Zlapany " + e); } } } Deklaracja finally Kod który będzie wykonany po bloku try/catch, bez względu na powstanie wyjątków. Użyteczny gdy należy zwolnić zasoby systemowe. Każdy try musi posiadać co najmniej jedną instrukcję catch lub finally. Demonstracja finally class FinallyDemo { static void procA() { try { System.out.println("wewnatrz procA"); throw new RuntimeException("demo"); } finally { System.out.println("procA: finally"); } } static void procB() { try { System.out.println("wewnatrz procB"); return; } finally { System.out.println("procB: finally"); } } Demonstracja finally } static void procC() { try { System.out.println("wewnatrz procC"); } finally { System.out.println("procC: finally"); } } public static void main(String args[]) { try { procA(); } catch(Exception e) { System.out.println("Wyjatek zlapany"); } procB(); procC(); } Standardowe Wyjątki Niesprawdzane RuntimeException są dostępne automatycznie. Nie muszą być deklarowane w sekcji throws. Kompilator nie sprawdza czy metoda deklaruje czy obsługuje te wyjątki. Inaczej wyjątki niesprawdzane. Standardowe Wyjątki Niesprawdzane ArithmeticException błędy arytmetyczne, np. dzielenie przez zero ● ArrayIndexOutOfBoundsException indeks tablicy poza zakresem ● ArrayStoreException przypisanie tablicy nieodpowiedniego typu elementu ● ClassCastException niepoprawne rzutowanie ● IllegalArgumentException niepoprawny argument metody ● IllegalMonitorStateException niepoprawna operacja monitora ● Standardowe Wyjątki Niesprawdzane IllegalStateException środowisko lub aplikacja jest w niepoprawnym stanie ● IllegalThreadStateException wymagana operacja niekompatybilna z bieżącym stanem wątka ● IndexOutOfBoundException jakis rodzaj indeksu jest poza zakresem ● NegativeArraySizeException tablica tworzona z ujemnym rozmiarem ● NullPointerException niepoprawne użycie wskaźnika null ● Standardowe Wyjątki Niesprawdzane NumberFormatException niepoprawna konwersja stringu na format liczbowy ● SecurityException próba naruszenia bezpieczeństwa ● StringIndexOutOfBoundsException indeks poza zakresem stringu ● UnsupportedOperationException napotkano niepoprawną operację ● Standardowe Wyjątki Sprawdzane Metody która mogą generować te wyjątki a nie potrafią ich samodzielnie obsłużyć, muszą je deklarować: ClassNotFoundException nie znaleziono klasy ● CloneNotSupportedException próba klonowania obiektu który nie implementuje interfejsu Cloneable. ● IllegalAccessException dostęp do klasy zabroniony ● InstantiationException próba tworzenia obiektu klasy abstrakcyjnej lub interfejsu ● Standardowe Wyjątki Sprawdzane InterruptedException jeden wątek przerwany przed drugi ● NoSuchFieldException pole nie istnieje ● NoSuchMethodException metoda nie istnieje ● Tworzenie Własnych Klas Wyjątków Utwórz pod-klasę Exception. Pod-klasa nie musi nic deklarować, musi tylko istnieć. Exception nie deklaruje własnych metod, ale dziedziczy po Throwable. Demonstracja Własnych Klas Wyjątków Własna klasa wyjątków: class MojWyjatek extends Exception { private int szczegoly; MojWyjatek(int a) { szczegoly = a; } Przesłonięcie metody z klasy Throwable: } public String toString() { return "MojWyjatek[" + szczegoly + "]"; } Demonstracja Własnych Klas Wyjątków class MojeWyjatki { Metoda deklaruje wyjątek tej klasy: static void oblicz(int a) throws MojWyjatek { System.out.println("oblicz(" + a + ")"); Wyrzucenie wyjątku: } if (a > 10) throw new MojWyjatek(a); System.out.println("Normalne wyjscie"); Demonstracja Własnych Klas Wyjątków public static void main(String args[]) { try { oblicz(1); oblicz(20); Przechwycenie wyjątku: } } } catch (MojWyjatek e) { System.out.println("Zlapany " + e); }