W życiu codziennym czasami zdarzają się sytuacje, których nie planowaliśmy. Na przykład wstajesz rano do pracy, szukasz ładowarki do telefonu, ale jej nie ma. Idziesz do łazienki umyć twarz – woda jest wyłączona. Wsiadłem do samochodu i nie odpalił. Ale człowiek jest w stanie dość łatwo poradzić sobie z takimi nieprzewidzianymi sytuacjami. W tym artykule postaramy się dowiedzieć, jak radzą sobie z nimi programy Java.
Jakie są wyjątki Java
W świecie programowania wystąpienie błędów i nieoczekiwanych sytuacji podczas wykonywania programu nazywa się wyjątkiem. W programie wyjątki mogą wystąpić w wyniku nieprawidłowych działań użytkownika, braku niezbędnego zasobu na dysku lub utraty połączenia z serwerem przez sieć. Wyjątki podczas wykonywania programu mogą być również spowodowane błędami programistycznymi lub nieprawidłowym użyciem API. W przeciwieństwie do naszego świata, program musi jasno wiedzieć, co zrobić w takiej sytuacji. W tym celu Java udostępnia mechanizm wyjątków.
Krótko o słowach kluczowych spróbuj, złap, wreszcie rzuty
Obsługa wyjątków w Javie opiera się na zastosowaniu w programie następujących słów kluczowych:
- try - definiuje blok kodu, w którym może wystąpić wyjątek;
- catch – definiuje blok kodu, w którym obsługiwany jest wyjątek;
- wreszcie – definiuje blok kodu, który jest opcjonalny, ale jeśli jest obecny, i tak jest wykonywany, niezależnie od wyników bloku try.
Te słowa kluczowe służą do tworzenia specjalnych konstrukcji przetwarzających w kodzie programu: try{}catch, try{}catch{}w końcu, try{}w końcu{}.
- rzut – używany do zgłoszenia wyjątku;
- rzuty – używane w sygnaturach metod, aby ostrzec, że metoda może zgłosić wyjątek.
Przykład użycia słów kluczowych w programie Java:
public String input() throws MyException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String s = null;
try {
s = reader.readLine();
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
try {
reader.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
if (s.equals("")) {
throw new MyException("String can not be empty!");
}
return s;
}
Dlaczego potrzebujemy mechanizmu wyjątków?
Spójrzmy na przykład z prawdziwego świata. Wyobraźmy sobie, że na autostradzie znajduje się odcinek z mostem awaryjnym o ograniczonej nośności. Jeśli najedzie na niego pojazd o masie przekraczającej nośność mostu, może się on zawalić, a sytuacja kierowcy może stać się, delikatnie mówiąc, wyjątkowa. Aby temu zapobiec, służby drogowe z wyprzedzeniem instalują na drodze znaki ostrzegawcze. Kierowca samochodu patrząc na znak ostrzegawczy porówna masę swojego samochodu z dopuszczalną masą przejazdu po moście. Jeśli przekroczy, pojedzie objazdem. Dzięki działaniom służb drogowych kierowcy ciężarówek, po pierwsze, otrzymali możliwość wcześniejszej zmiany trasy, po drugie, zostali ostrzeżeni o niebezpieczeństwie na głównej trasie, a wreszcie zostali uprzeczeni o niemożności korzystania z przejazdu. most pod pewnymi warunkami.
Możliwość zapobiegania i rozwiązywania wyjątków w programie, aby mógł on działać dalej, jest jednym z powodów używania wyjątków w Javie. Mechanizm wyjątków pozwala również chronić napisany kod (interfejs programowania) przed niewłaściwym użyciem przez użytkownika poprzez walidację (sprawdzanie) przychodzących danych. A teraz bądźmy na chwilę policją drogową. Na początek warto poznać miejsca, w których kierowcy mogą wpaść w kłopoty. Po drugie, musisz przygotować i zainstalować znaki ostrzegawcze. Na koniec należy zapewnić objazdy na wypadek zagrożenia na trasie głównej. W Javie mechanizm wyjątków działa w podobny sposób. Na etapie tworzenia programu „chronimy” niebezpieczne fragmenty kodu przed wyjątkami za pomocą bloku try{}, udostępniamy ścieżki „zapasowe” za pomocą bloku catch{}, a w bloku final{} piszemy kod, który jest wykonywany w program dla dowolnego rezultatu. W przypadkach, gdy nie możemy zapewnić „drogi awaryjnej” lub świadomie chcemy pozostawić wybór użytkownikowi, musimy przynajmniej ostrzec go o niebezpieczeństwie. Dlaczego? Wyobraźcie sobie oburzenie kierowcy, który dojeżdża do mostu awaryjnego, przez który nie da się przejechać, nie widząc po drodze ani jednego znaku ostrzegawczego! W programowaniu, pisząc nasze klasy i metody, nie zawsze możemy przewidzieć kontekst ich użycia przez innych programistów w ich programach, więc nie możemy przewidzieć w 100% prawidłowego sposobu rozwiązania wyjątku. Jednocześnie dobrą praktyką jest ostrzeganie użytkowników naszego kodu o możliwości wystąpienia wyjątku. Mechanizm wyjątków w Javie pozwala nam to zrobić za pomocą rzutów — zasadniczo deklarując ogólne zachowanie naszej metody w celu zgłoszenia wyjątku, pozostawiając w ten sposób użytkownikowi metody napisanie kodu obsługującego wyjątek w Javie.
Ostrzeżenie o „kłopotach”
Jeśli nie planujesz obsługiwać wyjątku w swojej metodzie, ale chcesz ostrzec użytkowników metody o możliwych wyjątkach, użyj słowa kluczowego rzuca. To słowo kluczowe w sygnaturze metody oznacza, że w pewnych warunkach metoda może zgłosić wyjątek. To ostrzeżenie jest częścią interfejsu metody i daje użytkownikowi prawo do dostosowania implementacji procedury obsługi wyjątków. Po rzutach wskazujemy typ zgłaszanego wyjątku. Są to zazwyczaj elementy potomne klasy
wyjątków Java . Ponieważ Java jest językiem obiektowym, wszystkie wyjątki w Javie są obiektami.
Hierarchia wyjątków Java
Gdy podczas wykonywania programu wystąpi błąd, środowisko wykonawcze JVM tworzy obiekt żądanego typu z hierarchii wyjątków Java - zestawu możliwych wyjątków odziedziczonych od wspólnego „przodka” - klasy Throwable. Wyjątkowe sytuacje występujące w programie można podzielić na dwie grupy:
- Sytuacje, w których przywrócenie dalszego normalnego działania programu jest niemożliwe
- Odzyskiwanie jest możliwe.
Do pierwszej grupy zaliczają się sytuacje, w których występują wyjątki odziedziczone z klasy
Error . Są to błędy, które pojawiają się podczas wykonywania programu w wyniku awarii maszyny JVM, przepełnienia pamięci lub awarii systemu. Zwykle wskazują na poważne problemy, których nie można naprawić za pomocą oprogramowania. Ten typ wyjątku w Javie jest klasyfikowany jako niesprawdzony na etapie kompilacji. Do tej grupy zalicza się również RuntimeException - wyjątki, dziedzice klasy
Wyjątek , generowane przez maszynę JVM podczas wykonywania programu. Często są one spowodowane błędami programistycznymi. Te wyjątki są również odznaczane w czasie kompilacji, więc pisanie kodu do ich obsługi nie jest konieczne. Do drugiej grupy zaliczają się sytuacje wyjątkowe, które są przewidziane na etapie pisania programu i dla których trzeba napisać kod przetwarzający. Takie wyjątki są sprawdzane. Większość pracy programisty Java związanego z wyjątkami polega na obsłudze takich sytuacji.
Tworzenie wyjątku
Podczas wykonywania programu maszyna JVM zgłasza wyjątek lub ręcznie za pomocą instrukcji
rzutu . Tworzy to obiekt wyjątku w pamięci i przerywa wykonywanie głównego kodu programu, podczas gdy moduł obsługi wyjątków JVM próbuje znaleźć sposób na obsłużenie wyjątku.
Obsługa wyjątków
Tworzenie bloków kodu, dla których zapewniamy obsługę wyjątków w Javie, odbywa się w programie za pomocą konstrukcji try{}catch, try{}catch{}finally, try{}finally{}.
Gdy w bloku try zostanie zgłoszony wyjątek, procedura obsługi wyjątku jest wyszukiwana w następującym bloku catch. Jeśli catch zawiera procedurę obsługi tego typu wyjątku, kontrola przechodzi do niego. Jeśli nie, JVM szuka procedury obsługi tego typu wyjątku w łańcuchu wywołań metod, aż do znalezienia odpowiedniego catch. Po wykonaniu bloku catch sterowanie przekazywane jest do opcjonalnego bloku
Final . Jeśli nie zostanie znaleziony odpowiedni blok catch, JVM zatrzymuje wykonywanie programu i wyświetla stos wywołań metod -
stack tracie , po wcześniejszym wykonaniu kodu bloku final, jeśli jest obecny. Przykład obsługi wyjątków:
public class Print {
void print(String s) {
if (s == null) {
throw new NullPointerException("Exception: s is null!");
}
System.out.println("Inside method print: " + s);
}
public static void main(String[] args) {
Print print = new Print();
List list= Arrays.asList("first step", null, "second step");
for (String s:list) {
try {
print.print(s);
}
catch (NullPointerException e) {
System.out.println(e.getMessage());
System.out.println("Exception was processed. Program continues");
}
finally {
System.out.println("Inside bloсk finally");
}
System.out.println("Go program....");
System.out.println("-----------------");
}
}
}
Wyniki metody
głównej :
Inside method print: first step
Inside bloсk finally
Go program....
-----------------
Exception: s is null!
Exception was processed. Program continues
Inside bloсk finally
Go program....
-----------------
Inside method print: second step
Inside bloсk finally
Go program....
-----------------
Blok
finally
jest zwykle używany do zamykania strumieni otwartych w bloku try lub do zwalniania zasobów. Jednak podczas pisania programu nie zawsze można śledzić zamknięcie wszystkich zasobów. Aby ułatwić nam życie, programiści Java zaproponowali nam konstrukcję
try-with-resources
, która automatycznie zamyka zasoby otwarte w bloku try. Nasz pierwszy przykład można przepisać w następujący sposób
try-with-resources
:
public String input() throws MyException {
String s = null;
try(BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))){
s = reader.readLine();
} catch (IOException e) {
System.out.println(e.getMessage());
}
if (s.equals("")){
throw new MyException ("String can not be empty!");
}
return s;
}
Dzięki możliwościom Javy, począwszy od wersji 7, możemy także połączyć w jednym bloku wychwytywanie różnych typów wyjątków, dzięki czemu kod będzie bardziej zwarty i czytelny. Na przykład:
public String input() {
String s = null;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
s = reader.readLine();
if (s.equals("")) {
throw new MyException("String can not be empty!");
}
} catch (IOException | MyException e) {
System.out.println(e.getMessage());
}
return s;
}
Wyniki
Zastosowanie wyjątków w Javie pozwala zwiększyć odporność programu na błędy poprzez zastosowanie ścieżek „zapasowych”, oddzielenie logiki kodu głównego od kodu obsługującego wyjątki poprzez zastosowanie bloków catch, a także daje nam możliwość możliwość delegowania obsługi wyjątków użytkownikowi naszego kodu za pomocą rzutów.
GO TO FULL VERSION