Wykład 8:
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.
Konstrukcje Obsługi Wyjątków
● try – otacza część programu, którą chcemymonitorować 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
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
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,
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;
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:
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;
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
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
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ę
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) {
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
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.
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
Standardowe Wyjątki Niesprawdzane
● IllegalStateException środowisko lub aplikacjajest 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
Standardowe Wyjątki Niesprawdzane
● NumberFormatException niepoprawna konwersjastringu na format liczbowy
● SecurityException próba naruszenia
bezpieczeństwa
● StringIndexOutOfBoundsException indeks poza
zakresem stringu
● UnsupportedOperationException napotkano
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
Standardowe Wyjątki Sprawdzane
● InterruptedException jeden wątek przerwany
przed drugi
● NoSuchFieldException pole 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
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); } } }