Programování

Výjimky v Javě, Část 1: Základy zpracování výjimek

Výjimkou Java jsou typy knihoven a jazykové funkce používané k reprezentaci a řešení selhání programu. Pokud jste chtěli pochopit, jak je selhání znázorněno ve zdrojovém kódu, jste na správném místě. Kromě přehledu výjimek Java vám představím jazykové funkce jazyka Java pro házení objektů, zkoušení kódu, který může selhat, chytání hozených objektů a vyčištění kódu Java po vyvolání výjimky.

V první polovině tohoto kurzu se dozvíte o základních jazykových funkcích a typech knihoven, které existují od verze Java 1.0. Ve druhé polovině objevíte pokročilé funkce představené v novějších verzích Java.

Všimněte si, že příklady kódu v tomto kurzu jsou kompatibilní s JDK 12.

stáhnout Získat kód Stáhněte si zdrojový kód například pro aplikace v tomto výukovém programu. Vytvořil Jeff Friesen pro JavaWorld.

Co jsou výjimky Java?

Selhání nastane, když je normální chování Java programu přerušeno neočekávaným chováním. Tato divergence je známá jako výjimka. Program se například pokusí otevřít soubor a přečíst jeho obsah, ale soubor neexistuje. Java klasifikuje výjimky do několika typů, takže zvažte každou z nich.

Zkontrolované výjimky

Java klasifikuje výjimky vyplývající z externích faktorů (například chybějící soubor) jako kontrolované výjimky. Kompilátor Java kontroluje, zda jsou takové výjimky zacházeno (opraveno) tam, kde se vyskytují nebo je dokumentováno, že mají být zpracovány jinde.

Obslužné rutiny výjimek

An obsluha výjimek je sekvence kódu, který zpracovává výjimku. Dotazuje se na kontext - to znamená, že čte hodnoty uložené z proměnných, které byly v rozsahu v době, kdy došlo k výjimce - a poté použije to, co se naučí, k obnovení normálního chování programu Java. Například obslužná rutina výjimky může číst uložený název souboru a vyzvat uživatele k nahrazení chybějícího souboru.

Runtime (nezaškrtnuté) výjimky

Předpokládejme, že se program pokusí rozdělit celé číslo na celé číslo 0. Tato nemožnost ilustruje další druh výjimky, jmenovitě a runtime výjimka. Na rozdíl od kontrolovaných výjimek, runtime výjimky obvykle vznikají ze špatně napsaného zdrojového kódu, a proto by je měl opravit programátor. Protože kompilátor nekontroluje, zda jsou výjimky za běhu zpracovány nebo zdokumentovány, aby byly zpracovány jinde, můžete si myslet na výjimku za běhu jako nezaškrtnutá výjimka.

O výjimkách za běhu

Můžete upravit program tak, aby zpracoval runtime výjimku, ale je lepší opravit zdrojový kód. Výjimky za běhu často vznikají z předávání neplatných argumentů metodám knihovny; buggy volací kód by měl být opraven.

Chyby

Některé výjimky jsou velmi závažné, protože ohrožují schopnost programu pokračovat v provádění. Například program se pokusí přidělit paměť z JVM, ale není dostatek volné paměti k uspokojení požadavku. Další vážná situace nastane, když se program pokusí načíst soubor třídy přes a Class.forName () volání metody, ale soubor třídy je poškozen. Tento druh výjimky se označuje jako chyba. Nikdy byste se neměli pokoušet vypořádat se s chybami sami, protože JVM by se z nich nemohl vzpamatovat.

Výjimky ve zdrojovém kódu

Výjimku lze ve zdrojovém kódu představit jako chybový kód nebo jako objekt. Představím obojí a ukážu vám, proč jsou objekty lepší.

Chybové kódy versus objekty

Programovací jazyky jako C používají celočíselné chybové kódy představovat selhání a důvody selhání - tj. výjimky. Zde je několik příkladů:

if (chdir ("C: \ temp")) printf ("Nelze změnit na dočasný adresář:% d \ n", errno); SOUBOR * fp = fopen ("C: \ temp \ foo"); if (fp == NULL) printf ("Nelze otevřít foo:% d \ n", errno);

C chdir () Funkce (změnit adresář) vrací celé číslo: 0 při úspěchu nebo -1 při selhání. Podobně C fopen () Funkce (file open) vrací nenulovou hodnotu ukazatel (celočíselná adresa) na a SOUBOR struktura na úspěch nebo nulový (0) ukazatel (reprezentovaný konstantou NULA) při selhání. V obou případech, abyste identifikovali výjimku, která způsobila selhání, musíte přečíst globální errno celočíselný chybový kód proměnné.

Chybové kódy představují určité problémy:

  • Celá čísla nemají smysl; nepopisují výjimky, které představují. Co například znamená 6?
  • Přiřazení kontextu k chybovému kódu je nepříjemné. Můžete například chtít načíst název souboru, který nelze otevřít, ale kam chcete uložit název souboru?
  • Celá čísla jsou libovolná, což může vést ke zmatku při čtení zdrojového kódu. Například zadání if (! chdir ("C: \ temp")) (! znamená NE) namísto if (chdir ("C: \ temp")) testovat selhání je jasnější. Pro označení úspěchu však byla zvolena 0 atd if (chdir ("C: \ temp")) musí být specifikováno pro testování selhání.
  • Chybové kódy je příliš snadné ignorovat, což může vést k chybovému kódu. Například může programátor určit chdir ("C: \ temp"); a ignorovat if (fp == NULL) šek. Programátor navíc nemusí zkoumat errno. Při netestování selhání se program chová nestandardně, když některá funkce vrací indikátor selhání.

K vyřešení těchto problémů přijala Java nový přístup ke zpracování výjimek. V Javě kombinujeme objekty, které popisují výjimky, s mechanismem založeným na házení a chytání těchto objektů. Zde jsou některé výhody použití objektů oproti chybovému kódu k označení výjimek:

  • Objekt lze vytvořit ze třídy se smysluplným názvem. Například, FileNotFoundException (v java.io balíček) je smysluplnější než 6.
  • Objekty mohou ukládat kontext do různých polí. V polích objektu můžete například uložit zprávu, název souboru, který nelze otevřít, nejnovější pozici, kde operace analýzy selhala, a / nebo další položky.
  • Nepoužíváte -li prohlášení k testování selhání. Místo toho jsou objekty výjimky vyvolány obslužné rutině, která je oddělená od kódu programu. Výsledkem je, že zdrojový kód je čitelnější a méně pravděpodobné, že bude chybný.

Vrhací a jeho podtřídy

Java poskytuje hierarchii tříd, které představují různé druhy výjimek. Tyto třídy jsou zakořeněny v java.lang balíček Vrhací třídy, spolu s jeho Výjimka, RuntimeException, a Chyba podtřídy.

Vrhací je nejvyšší nadtřída, pokud jde o výjimky. Pouze objekty vytvořené z Vrhací a jeho podtřídy mohou být hozeny (a následně chyceny). Takové objekty jsou známé jako házené předměty.

A Vrhací objekt je spojen s a podrobná zpráva který popisuje výjimku. K vytvoření a je poskytnuto několik konstruktorů, včetně dvojice popsané níže Vrhací objekt s podrobnou zprávou nebo bez ní:

  • Vrhací () vytváří Vrhací bez podrobných zpráv. Tento konstruktor je vhodný pro situace, kde není žádný kontext. Například chcete jen vědět, že zásobník je prázdný nebo plný.
  • Throwable (String message) vytváří Vrhací s zpráva jako podrobná zpráva. Tuto zprávu lze odeslat uživateli nebo přihlásit.

Vrhací poskytuje Řetězec getMessage () metoda pro vrácení podrobné zprávy. Poskytuje také další užitečné metody, které uvedu později.

Třída výjimek

Vrhací má dvě přímé podtřídy. Jedna z těchto podtříd je Výjimka, který popisuje výjimku vyplývající z externího faktoru (například pokus o čtení z neexistujícího souboru). Výjimka deklaruje stejné konstruktory (se stejnými seznamy parametrů) jako Vrhacía každý konstruktor vyvolá své Vrhací protějšek. Výjimka dědí Vrhacímetody; deklaruje žádné nové metody.

Java poskytuje mnoho tříd výjimek, které přímo podtřídu Výjimka. Tady jsou tři příklady:

  • CloneNotSupportedException signalizuje pokus o klonování objektu, jehož třída neimplementuje Cloneable rozhraní. Oba typy jsou v java.lang balík.
  • IOException signalizuje, že došlo k nějakému selhání I / O. Tento typ se nachází v java.io balík.
  • ParseException signalizuje, že při analýze textu došlo k chybě. Tento typ naleznete v java.text balík.

Všimněte si, že každý Výjimka název podtřídy končí slovem Výjimka. Tato konvence usnadňuje identifikaci účelu třídy.

Obvykle budete podtřídy Výjimka (nebo jedna z jejích podtříd) s vašimi vlastními třídami výjimek (jejichž jména by měla končit na Výjimka). Zde je několik příkladů vlastních podtříd:

public class StackFullException extends Exception {} public class EmptyDirectoryException extends Exception {private String directoryName; public EmptyDirectoryException (String message, String directoryName) {super (zpráva); this.directoryName = adresář; } public String getDirectoryName () {návrat adresářeName; }}

První příklad popisuje třídu výjimek, která nevyžaduje podrobnou zprávu. Je to výchozí vyvolání konstruktoru noargument Výjimka(), který vyvolá Vrhací ().

Druhý příklad popisuje třídu výjimky, jejíž konstruktor vyžaduje podrobnou zprávu a název prázdného adresáře. Konstruktor vyvolá Výjimka (řetězcová zpráva), který vyvolá Throwable (String message).

Objekty vytvořené z Výjimka nebo jedna z jejích podtříd (kromě RuntimeException nebo jedna z jejích podtříd) jsou zaškrtnuté výjimky.

RuntimeException Třída

Výjimka je přímo podtřída RuntimeException, který popisuje výjimku s největší pravděpodobností vyplývající ze špatně napsaného kódu. RuntimeException deklaruje stejné konstruktory (se stejnými seznamy parametrů) jako Výjimkaa každý konstruktor vyvolá své Výjimka protějšek. RuntimeException dědí Vrhacímetody. Deklaruje žádné nové metody.

Java poskytuje mnoho tříd výjimek, které přímo podtřídu RuntimeException. Následující příklady jsou všichni členové java.lang balík:

  • Aritmetická výjimka signalizuje neplatnou aritmetickou operaci, jako je pokus o dělení celého čísla číslem 0.
  • IllegalArgumentException signalizuje, že metodě byl předán nezákonný nebo nevhodný argument.
  • NullPointerException signalizuje pokus o vyvolání metody nebo o přístup k poli instance pomocí nulové reference.

Objekty vytvořené z RuntimeException nebo jedna z jejích podtříd je nezaškrtnuté výjimky.

Třída Error

VrhacíDalší přímá podtřída je Chyba, který popisuje závažný (i abnormální) problém, se kterým by se rozumná aplikace neměla snažit vypořádat - například nedostatek paměti, přetečení zásobníku JVM nebo pokus o načtení třídy, kterou nelze najít. Jako Výjimka, Chyba deklaruje identické konstruktory Vrhací, dědí Vrhacímetody a nedeklaruje žádnou ze svých vlastních metod.

Můžete identifikovat Chyba podtřídy z konvence, jejichž názvy tříd končí Chyba. Mezi příklady patří OutOfMemoryError, Chyba spojení, a StackOverflowError. Všechny tři typy patří do java.lang balík.

Házení výjimek

Funkce knihovny C upozorní volající kód výjimky nastavením globálu errno proměnnou na kód chyby a vrácení kódu chyby. Naproti tomu metoda Java hodí objekt. Vědět, jak a kdy vyvolávat výjimky, je základním aspektem efektivního programování v jazyce Java. Vyvolání výjimky zahrnuje dva základní kroky:

  1. Použijte házet příkaz k vyvolání objektu výjimky.
  2. Použijte hodí klauzule informovat kompilátor.

Pozdější části se zaměří na zachycení výjimek a vyčištění po nich, ale nejprve se pojďme dozvědět více o hodu.

Prohlášení hodu

Java poskytuje házet příkaz k vyvolání objektu, který popisuje výjimku. Tady je syntaxe házet prohlášení :

házet házet;

Objekt identifikovaný pomocí házet je instancí Vrhací nebo některou z jejích podtříd. Obvykle však házíte pouze objekty vytvořené z podtříd třídy Výjimka nebo RuntimeException. Zde je několik příkladů:

hodit novou FileNotFoundException ("nelze najít soubor" + název souboru); házet novou IllegalArgumentException ("argument předaný do počtu je menší než nula");

Vrhací hod je vyvolán z aktuální metody do JVM, který kontroluje tuto metodu pro vhodnou obslužnou rutinu. Pokud není nalezen, JVM odvíjí zásobník volání metody a hledá nejbližší volací metodu, která dokáže zpracovat výjimku popsanou v hodu. Pokud najde tuto metodu, předá vyvolávací metodu obslužné rutině metody, jejíž kód je spuštěn pro zpracování výjimky. Pokud není nalezena žádná metoda pro zpracování výjimky, JVM končí vhodnou zprávou.

Klauzule hází

Je třeba informovat kompilátor, když vyhodíte kontrolovanou výjimku z metody. Udělejte to připojením a hodí klauzule do záhlaví metody. Tato klauzule má následující syntaxi:

hodí checkedExceptionClassName (, checkedExceptionClassName)*

A hodí klauzule se skládá z klíčového slova hodí následuje čárkami oddělený seznam názvů tříd kontrolovaných výjimek vyhozených z metody. Zde je příklad:

public static void main (String [] args) hodí ClassNotFoundException {if (args.length! = 1) {System.err.println ("usage: java ... classfile"); vrátit se; } Class.forName (args [0]); }

Tento příklad se pokusí načíst soubor třídy identifikovaný argumentem příkazového řádku. Li Class.forName () nemůže najít třídní soubor, hodí a java.lang.ClassNotFoundException objekt, což je zaškrtnutá výjimka.

Zkontrolovaná kontroverze výjimek

The hodí klauzule a kontrolované výjimky jsou kontroverzní. Mnoho vývojářů nenávidí, když jsou nuceni specifikovat hodí nebo zpracovat zaškrtnuté výjimky. Další informace o tom z mého Jsou kontrolované výjimky dobré nebo špatné? příspěvek na blogu.