Přidány poznámky do nadpisu 24.4.

This commit is contained in:
Vojtěch Pour 2022-12-18 19:05:52 +01:00
parent 2e55561987
commit 4d00c94f36

View file

@ -1910,4 +1910,151 @@ typ nebo třída)
- Hlavička třídy, jejíž instance jsou v prohledávaném poli, musí být ```public Třída implements Comparable<Třída>```
- Musí obsahovat metodu ```public int compareTo(Třída třída)```
- Podrobnosti viz předměty KIV/PPA2 a KIV/OOP
#### Mechanizmus předání referenčních parametrů metod
## Výjimky
- Mechanizmus výjimek je silným bezpečnostním mechanizmem nejen v Javě, ale i v mnoha jiných jazycích
- Umožňuje (a v některých případech vynucuje) řešení chybových stavů, které za běhu programu mohou z různých důvodů nastat
### Základní pojmy
- **Výjimka** (exception)
- Též výjimečný stav (exceptional event) či (nepřesně) chyba (error)
- **Událost, u které si nepřejeme, aby v programu vznikla**
- Např. očekáváme od uživatele jako vstup celé číslo, ale on zadá reálné
- Bez výjimky by program fungoval lépe podle očekávání
- **Vyhození výjimky** (throw exception)
- Okamžik, kdy výjimka vznikne
#### Popis výjimky
- **Výjimka** v Javě je **instance konkrétní speciální třídy**
- Samotná třída výjimky udává, k jaké chybě došlo
- Obsahuje upřesňující informace
- Přesnější popis chyby
- Obsah zásobníku (stack trace)
### Druhy výjimek
- Existují **dva** základní druhy výjimek
- **Exception**
- Je NUTNÉ je ošetřit ve zdrojovém kódu
- **RuntimeException**
- Je MOŽNÉ je ošetřit ve zdrojovém kódu, ale není to nutné
- Způsob práce s oběma typy je zcela stejný
- Od základních typů odvozeno (odděděno) mnoho dalších výjimek, které jsou více specifické
- Výjimky mají hierarchickou strukturu, na vrcholu je ```Exception```
- Protože všechny jsou původně odvozené (odděděné) od základních, je možné při ošetřování výjimek používat názvy nadřazených nebo základních druhů
- Např. ```FileNotFoundException``` (soubor nenalezen) je možné nahradit ```IOException``` (obecná chyba vstupu a výstupu) nebo rovnou ```Exception```
#### Výjimky (Exception)
- Též kontrolované výjimky (checked exceptions)
- Všechny tyto výjimky jsou odvozené (přímo nebo přes delší hierarchii) od ```Exception``` a nikde v hierarchii odvození (dědění) se nevyskytuje ```RuntimeException```
- Je nutné se o ně v programu postarat, tj. ošetřit je přidáním speciální části zdrojového kódu
- Vyskytují se v souvislosti s voláním metod, u kterých je zvýšená pravděpodobnost, že se při jejich použití může vyskytnout problém
- Typicky metody, které pracují se vstupy a výstupy
- Tyto metody jsou napsány tak, že při jejich použití je nutné na potenciální nebezpečí reagovat
- Pokud není přidána část kódu pro ošetření případné výjimky, program nejde ani přeložit
#### Výjimky za běhu (RuntimeException)
- Všechny tyto výjimky jsou odvozeny (přímo nebo přes delší hierarchii) od ```RuntimeException```
- Není nutné se o ně v programu postarat, protože mohou nastat kdekoliv
- Příkladem je přístup do neexistujícího prvku pole (```ArrayIndexOutOfBoundException```), volání metody nad referenční proměnnou, která je ```null``` (```NullPointerException```),dělení nulou (```ArithmeticException```) a mnoho dalších
- Program lze přeložit **překladač nás nenutí, abychom je ošetřili** pomocí speciální části zdrojového kódu
- Považujeme-li to za vhodné, můžeme je ošetřit (stejným způsobem jako kontrolované výjimky)
- Typicky je to v místech, kde je zvýšená pravděpodobnost problému (např. vstupy od uživatele)
- Nemá větší smysl ošetřovat tyto výjimky v místě, kde jsme si stoprocentně jisti, že výjimka nemůže nastat (nemůže být vyhozena)
- Přesto se může stát, že výjimka v tomto místě nastane, protože jsme nezvážili všechny možnosti
- Pokud je výjimka za běhu programu vyhozena a není ošetřena, dojde k předčasnému ukončení programu a výpisu výjimky na standardní chybový výstup (na obrazovku)
- Z chybového výpisu lze poznat, kde k výjimce došlo
- Dalším krokem je oprava programu odstranění důvodu výjimky nebo její ošetření
### Reakce na výjimku
- Existují tři základní přístupy, jak reagovat na výjimku
- Předání výjimky výše
- Programátor výjimku nechce (nebo neumí) ošetřovat v místě vyhození, a proto informaci o jejím výskytu předá do nadřazené úrovně (tzn. do volající metody)
- Kompletní ošetření výjimky
- Výjimka je zachycena a ošetřena v místě vyhození (vzniku)
- Kombinace obou přístupů
- Výjimka je zachycena a (částečně či kompletně) ošetřena v místě vyhození, ale informace o ní se navíc předá do nadřazené úrovně (tzn. do volající metody)
- Všechny tyto postupy se běžně používají
- Často se využívá nejprve předání výjimky výše a následně její ošetření ve vyšší úrovni
#### Předání výjimky výše (deklarace výjimky)
- Metoda, ve které se výjimka vyskytla (byla vyhozena), se zříká odpovědnosti za její zpracování
- Předání odpovědnosti výše se deklaruje v hlavičce metody
- Tento postup je velice jednoduchý, ale pouze odsouvá řešení problému, které bude muset být nakonec provedeno (ale toto řešení může být např. provedeno na vhodnějším místě, než je metoda, kde výjimka vznikla)
- Na první pohled nejjednodušší řešení
- Není třeba žádných zásahů do těla metody
- Do hlavičky metody se přidá ```throws NázevVýjimky```
- Např. ```throws IOException```
#### Kompletní ošetření výjimky
- Případná vyhozená výjimka nepronikne ven z metody
- Nadřazená úroveň (volající metoda) se nemusí o nic starat
- Ošetření výjimky se provede pomocí konstrukce ```try-catch-finally```
- Vlastní výkonný kód se nemění, jen se uzavře do bloku začínajícího klíčovým slovem ```try```
- Prakticky je často nutné přesunout některé deklarace vně bloku ```try```
- V tomto bloku předpokládáme vznik výjimky
- Výjimka vždy vznikne na konkrétní řádce => touto řádkou skončí vykonávání bloku ```try```, pokud k výjimce dojde
- Blok ```catch```, který za blokem ```try``` bezprostředně následuje, určuje, na jakou výjimku se bude reagovat a jak
- Kód v bloku ```catch``` se provede pouze v případě, že k uvedené výjimce (někde v bloku ```try```) dojde
- Bloků ```catch``` může být více, každý může reagovat na jinou výjimku
- Blok ```catch``` reaguje pouze na výjimku, která je deklarovaná v závorce a všechny výjimky od ní odvozené (potomky)
- Pokud nastane jiná výjimka, blok ```catch``` se neprovede
- Pokud není uveden žádný vhodný blok ```catch```, výjimka není odchycena
- Blok ```finally```
- Blok ```finally``` následuje za blokem catch
- Obsahuje část kódu, která se provede vždy ať už k výjimce dojde, nebo ne (i když dojde k jiné výjimce, která není odchycena)
- Nepoužívá se příliš často
- Možné ošetření výjimky pouze vypsání chyby a ukončení programu Primitivní, ale často rozumný způsob
- Hodí se v případech, kdy vznik výjimky znamená, že program dál stejně nemůže smysluplně pokračovat
- Umožní program přeložit (pokud ošetřujeme kontrolovanou výjimku)
- V případě, že je výjimka vyhozena, se o ní z chybového výpisu dozvíme
- Pokud ošetřujeme výjimku přímo v místě vzniku v nějaké hluboce zanořené metodě, je to často jediný smysluplný způsob kompletního ošetření výjimky
- Vhodnější alternativa je předání výjimky výše a její ošetření na vyšší úrovni, kde se dá smysluplně reagovat
- Ošetření více různých výjimek
- Za blokem ```try``` může následovat více bloků ```catch```, odchytávajících různé výjimky
- Je tak možné reagovat různě podle toho, k jaké výjimce došlo
- Je možné reagovat i na více různých výjimek stejně
- Je možné uvést výjimku, která je společným předkem všech výjimek, na které chceme reagovat stejně často to může být přímo ```Exception```
- V tom případě jsou ale odchyceny všechny výjimky (i ty, o kterých jsme neuvažovali) a to není žádoucí
- Je možné uvést více výjimek do jednoho bloku ```catch```
- Výjimky jsou odděleny svislítkem „|“
- Lepší postup, protože jsou zachycené pouze uvedené výjimky (a výjimky od nich odvozené), ale žádné další
#### Předání výjimky výše a její ošetření výše
- Běžný postup
- Výjimka je z metod na nižší úrovni (např. zajišťující čtení ze souboru) propagována výše, až do úrovně, kde je vhodné ji ošetřit, a tam je výjimka vhodně ošetřena (např. v GUI zobrazením dialogu s varováním)
#### Nejhorší reakce na výjimku
- Důvod je v naprosté většině případů lenost programátora
- Nechce výjimku propagovat, protože by to následně musel udělat i ve všech volajících metodách
- Použije se konstrukce ```try-catch```, ale do bloku ``catch`` se nic nenapíše
- Zmíněným postupem jsme se připravili o všechny výhody mechanizmu výjimek a vlastně jsme situaci ještě zhoršili
- Výjimka proběhne, ale nikdo se nedozví kdy a kde
- Někdy je však skutečně potřeba na zachycenou výjimku nijak nereagovat a také ji nepropagovat
- Typickým příkladem je odchycení ```InterruptedException``` při synchronizaci vláken (viz předmět KIV/PGS)
- V tom případě je možné nechat blok ```catch``` bez výkonného kódu, ale je nutné doplnit dostatečně podrobný vysvětlující komentář, proč tomu tak je
- Je také možné převést kontrolovanou výjimku na výjimku za běhu
#### Konstrukce try-with-resources
- Po skončení práce se soubory (čtení/zápis) je obecně dobrým zvykem soubor zavřít metodou ```close()```
- V předchozích příkladech nebylo použito
- Protože to nebylo z hlediska výkladu podstatné a bude to podrobněji zmíněno později
- Protože neuzavřením souboru ```data.txt``` se ve skutečnosti nic špatného nestane
- Byl otevřen pouze pro čtení
- JVM při ukončení programu (ať už s výjimkou nebo bez) automaticky uzavře všechny soubory, které program otevřel
- Neuzavření souboru však může vadit např. při zápisu do něj, a proto je rozumné soubory uzavírat vždy
- Čtení nebo zápis do souboru může často skončit výjimkou
- Pokud se vyskytne výjimka, program neprovede část kódu uvedenou za řádkou, kde výjimka nastala
- Buď okamžitě ukončí metodu (pokud je výjimka předána výše) nebo skočí do odpovídajícího bloku ```catch```
- V této části kódu (která se při vyhození výjimky neprovede) se typicky vyskytuje volání metody ```close() ```pro uzavření souborů, které se tím pádem při výskytu výjimky neprovede
- Řešení
- Přesunout volání ```close()``` do bloku ```finally```
- Běžný postup, ale metoda ```close()``` může také vyhodit výjimku a tu je třeba ošetřit
- Využít konstrukci ```try```-with-resources
- Konstrukce ```try```-with-resources
- Umožňuje automatické uzavření souboru, bez ohledu na to, zda k výjimce došlo nebo ne
- Instance použitá pro práci se souborem (např. instance třídy ```Scanner```) se uvede do kulatých závorek za klíčové slovo ```try```
- Třída musí implementovat rozhraní ```AutoCloseable```
- Splňují knihovní třídy pro práci se soubory
- V závorce za klíčovým slovem ```try``` může být uvedeno i více instancí různých tříd, jednotlivé deklarace jsou odděleny středníkem
### Ruční vyhození výjimky