Iznimke u Javi, 1. dio: Osnove rukovanja iznimkama

Iznimke Java su vrste biblioteka i jezične značajke koje se koriste za predstavljanje i rješavanje problema programa. Ako ste željeli razumjeti kako se neuspjeh predstavlja u izvornom kodu, došli ste na pravo mjesto. Pored pregleda Java izuzetaka, započet ću s jezičnim značajkama Java za bacanje predmeta, isprobavanje koda koji može propasti, hvatanje bačenih predmeta i čišćenje Java koda nakon što je izuzetak izbačen.

U prvoj polovici ovog vodiča naučit ćete o osnovnim jezičnim značajkama i vrstama knjižnica koje postoje od Jave 1.0. U drugoj polovici otkrit ćete napredne mogućnosti predstavljene u novijim verzijama Jave.

Primijetite da su primjeri koda u ovom vodiču kompatibilni s JDK 12.

preuzimanje Preuzmite kod Preuzmite izvorni kod za primjere aplikacija u ovom vodiču. Stvorio Jeff Friesen za JavaWorld.

Koji su Java izuzeci?

Neuspjeh se događa kada je normalno ponašanje Java programa prekinuto neočekivanim ponašanjem. Ova je divergencija poznata kao iznimka . Na primjer, program pokušava otvoriti datoteku kako bi pročitao njezin sadržaj, ali datoteka ne postoji. Java klasificira iznimke na nekoliko vrsta, pa razmotrimo svaku od njih.

Označene iznimke

Java klasificira iznimke koje proizlaze iz vanjskih čimbenika (poput datoteke koja nedostaje) kao provjerene iznimke . Java kompajler provjerava jesu li takve iznimke obrađene (ispravljene) tamo gdje se pojave ili dokumentirane da bi se mogle rukovati negdje drugdje.

Rukovoditelji iznimkama

Iznimka rukovatelj je slijed koda koji obrađuje iznimku. Ispituje kontekst - što znači da čita vrijednosti spremljene iz varijabli koje su bile u opsegu u vrijeme kada se dogodila iznimka -, a zatim koristi ono što nauči za vraćanje Java programa u tok normalnog ponašanja. Na primjer, rukovatelj iznimkom može pročitati spremljeno ime datoteke i zatražiti od korisnika da zamijeni datoteku koja nedostaje.

Iznimke tijekom izvođenja (neprovjereno)

Pretpostavimo da program pokušava podijeliti cijeli broj sa cijelim brojem 0. Ova nemogućnost ilustrira drugu vrstu iznimke, naime iznimku u vrijeme izvođenja . Za razliku od provjerenih iznimaka, izuzeci u vrijeme izvođenja obično proizlaze iz loše napisanog izvornog koda i programer bi ih stoga trebao popraviti. Budući da kompajler ne provjerava jesu li izuzeci izvršavanja obrađeni ili dokumentirani da bi se njima rukovalo negdje drugdje, možete smatrati izuzetak izvršavanja kao neprovjerenu iznimku .

O izuzecima tijekom izvođenja

Možete izmijeniti program tako da obrađuje izuzetak tijekom izvođenja, ali bolje je popraviti izvorni kod. Iznimke tijekom izvođenja često nastaju prenošenjem nevaljanih argumenata u metode knjižnice; kod za pozivanje pogreške treba popraviti.

Pogreške

Neke su iznimke vrlo ozbiljne jer ugrožavaju sposobnost programa da nastavi izvršavanje. Na primjer, program pokušava dodijeliti memoriju iz JVM-a, ali nema dovoljno slobodne memorije da udovolji zahtjevu. Sljedeća se ozbiljna situacija događa kada program pokušava učiti datoteku predavanja putem Class.forName()poziva metode, ali datoteka klase je oštećena. Ova vrsta iznimke poznata je kao pogreška . Nikada ne biste trebali pokušavati sami rješavati pogreške jer se JVM možda neće moći oporaviti od njih.

Iznimke u izvornom kodu

Iznimka se u izvornom kodu može prikazati kao kôd pogreške ili kao objekt . Predstavit ću vam obje i pokazati vam zašto su objekti superiorniji.

Kodovi pogrešaka u odnosu na objekte

Programski jezici kao što je C koriste cjelovite kodove pogrešaka za predstavljanje neuspjeha i razloga za neuspjeh - tj. Iznimke. Evo nekoliko primjera:

if (chdir("C:\\temp")) printf("Unable to change to temp directory: %d\n", errno); FILE *fp = fopen("C:\\temp\\foo"); if (fp == NULL) printf("Unable to open foo: %d\n", errno);

chdir()Funkcija C (promjena direktorija) vraća cijeli broj: 0 u slučaju uspjeha ili -1 u slučaju neuspjeha. Slično tome, fopen()funkcija C (datoteka otvorena) vraća neuspjeli pokazivač (cjelobrojnu adresu) FILEstrukturi u slučaju uspjeha ili null (0) pokazivač (predstavljen konstantom NULL) u slučaju neuspjeha. U oba slučaja, da biste identificirali iznimku koja je uzrokovala neuspjeh, morate pročitati errnokod pogreške zasnovan na cijelom broju globalne varijable.

Kodovi pogrešaka predstavljaju neke probleme:

  • Cijeli brojevi su besmisleni; ne opisuju iznimke koje predstavljaju. Na primjer, što znači 6?
  • Povezivanje konteksta s kodom pogreške je nezgodno. Na primjer, možda biste željeli ispisati ime datoteke koju nije moguće otvoriti, ali gdje ćete pohraniti ime datoteke?
  • Cijeli brojevi su proizvoljni, što može dovesti do zabune prilikom čitanja izvornog koda. Na primjer, jasnije je navođenje if (!chdir("C:\\temp"))( !znači NE) umjesto if (chdir("C:\\temp"))testiranja na neuspjeh. Međutim, 0 je odabran da ukazuje na uspjeh i if (chdir("C:\\temp"))mora biti naveden za testiranje neuspjeha.
  • Kodove pogrešaka previše je jednostavno zanemariti, što može dovesti do pogrešnog koda. Na primjer, programer je mogao odrediti chdir("C:\\temp");i zanemariti if (fp == NULL)provjeru. Nadalje, programer ne mora ispitivati errno. Ne testiranjem kvara, program se ponaša pogrešno kada bilo koja funkcija vrati indikator kvara.

Kako bi riješila ove probleme, Java je prihvatila novi pristup rukovanju iznimkama. U Javi kombiniramo objekte koji opisuju iznimke s mehanizmom koji se temelji na bacanju i hvatanju tih predmeta. Evo nekoliko prednosti korištenja objekata naspram koda pogreške za označavanje iznimaka:

  • Objekt se može stvoriti iz klase sa značajnim imenom. Na primjer, FileNotFoundException(u java.iopaketu) je značajniji od 6.
  • Objekti mogu pohraniti kontekst u raznim poljima. Na primjer, možete pohraniti poruku, ime datoteke koju nije moguće otvoriti, najnovije mjesto na kojem nije uspjela operacija raščlanjivanja i / ili druge stavke u polja objekta.
  • Ne koristite ifizjave za testiranje neuspjeha. Umjesto toga, objekti iznimke bacaju se na rukovatelj koji je odvojen od programskog koda. Kao rezultat toga, izvorni je kod lakši za čitanje i manje je vjerojatno da će biti prisluškivan.

Mogućnost bacanja i njegove potklase

Java pruža hijerarhiju klasa koje predstavljaju različite vrste iznimaka. Ti razredi su ukorijenjene u java.langpaketu u Throwablerazredu, zajedno sa svojim Exception, RuntimeExceptioni Errorpodrazreda.

Throwableje krajnja superklasa kada su u pitanju iznimke. ThrowableMogu se bacati (i naknadno hvatati) samo objekti stvoreni iz i njegove potklase. Takvi su predmeti poznati kao bacači .

ThrowableObjekt povezan s detaljima poruku koja opisuje iznimku. Nekoliko konstruktora, uključujući dolje opisani par, predviđeno je za stvaranje Throwableobjekta sa ili bez detaljne poruke:

  • Throwable () stvara Throwableporuku bez detalja. Ovaj konstruktor prikladan je za situacije u kojima nema konteksta. Na primjer, želite znati samo da je stog prazan ili pun.
  • Za odbaciti (Niz poruka) stvara Throwables messagekao detalj poruke. Ova se poruka može poslati korisniku i / ili prijaviti.

Throwablepruža String getMessage()metodu za vraćanje poruke s detaljima. Također pruža dodatne korisne metode koje ću predstaviti kasnije.

Klasa Izuzetak

Throwableima dvije izravne potklase. Jedna od ovih potklasa je Exception, koja opisuje iznimku koja proizlazi iz vanjskog čimbenika (kao što je pokušaj čitanja iz nepostojeće datoteke). Exceptiondeklarira iste konstruktore (s identičnim popisima parametara) kao Throwablei svaki konstruktor poziva svoj Throwablekolegu. Exceptionnasljeđuje Throwablemetode; ne izjavljuje nove metode.

Java nudi mnoge klase iznimki koje se izravno potklase Exception. Evo tri primjera:

  • CloneNotSupportedException signalizira pokušaj kloniranja objekta čija klasa ne implementira Cloneablesučelje. Obje vrste su u java.langpaketu.
  • IOException signalizira da je došlo do neke vrste I / O kvara. Ova se vrsta nalazi u java.iopaketu.
  • ParseException signalizira da je došlo do kvara tijekom raščlanjivanja teksta. Ovu vrstu možete pronaći u java.textpaketu.

Primijetite da svaki Exceptionnaziv podrazreda završava riječju Exception. Ova konvencija olakšava prepoznavanje svrhe razreda.

Tipično ćete podrazred Exception(ili jednu od njegovih potklasa) imati sa svojim vlastitim klasama iznimki (čija bi imena trebala završavati Exception). Evo nekoliko primjera prilagođenih potklasa:

public class StackFullException extends Exception { } public class EmptyDirectoryException extends Exception { private String directoryName; public EmptyDirectoryException(String message, String directoryName) { super(message); this.directoryName = directoryName; } public String getDirectoryName() { return directoryName; } }

Prvi primjer opisuje klasu iznimke koja ne zahtijeva detaljnu poruku. To je zadani konstruktor noargument koji poziva Exception(), koji poziva Throwable().

Drugi primjer opisuje klasu iznimke čiji konstruktor zahtijeva detaljnu poruku i ime praznog direktorija. Konstruktor poziva Exception(String message), koji poziva Throwable(String message).

Objekti instancirani iz Exceptionili iz jedne od njegovih potklasa (osim za RuntimeExceptionjednu ili podklasu) označene su kao iznimke.

Klasa RuntimeException

Exceptionje izravno potklasirano sa RuntimeException, što opisuje iznimku koja je vjerojatno nastala zbog loše napisanog koda. RuntimeExceptiondeklarira iste konstruktore (s identičnim popisima parametara) kao Exceptioni svaki konstruktor poziva svoj Exceptionkolegu. RuntimeExceptionnasljeđuje Throwablemetode. Ne objavljuje nove metode.

Java nudi mnoge klase iznimki koje se izravno potklase RuntimeException. Sljedeći primjeri su svi članovi java.langpaketa:

  • ArithmeticException signalizira nezakonitu aritmetičku operaciju, poput pokušaja dijeljenja cijelog broja s 0.
  • IllegalArgumentException signalizira da je ilegalni ili neprimjereni argument proslijeđen metodi.
  • NullPointerException signalizira pokušaj pozivanja metode ili pristupa polju instance putem null reference.

Objekti instancirani iz RuntimeExceptionili iz jedne od njegovih potklasa su neprovjerene iznimke .

Klasa pogreške

ThrowableDruga izravna potklasa je Error, koja opisuje ozbiljan (čak i nenormalan) problem koji razumna aplikacija ne bi trebala pokušati riješiti - poput nestanka memorije, prekomjernog JVM-ovog stoga ili pokušaja učitavanja klase koju nije moguće pronaći. Kao Exception, Errordeklarira identične konstruktore Throwable, nasljeđuje Throwablemetode i ne deklarira niti jednu vlastitu metodu.

Možete prepoznati Errorpotklase iz konvencije kojima završavaju njihova imena klasa Error. Primjeri uključuju OutOfMemoryError, LinkageErrori StackOverflowError. Sve tri vrste pripadaju java.langpaketu.

Bacanje iznimaka

Funkcija AC knjižnice obavještava pozivni kod o iznimci postavljanjem globalne errnovarijable na kôd pogreške i vraćanjem koda kvara. Suprotno tome, Java metoda baca objekt. Znanje kako i kada treba izbaciti iznimke bitan je aspekt učinkovitog Java programiranja. Bacanje iznimke uključuje dva osnovna koraka:

  1. Koristite throwizjavu za bacanje objekta iznimke.
  2. Koristite throwsklauzulu za obavještavanje sastavljača.

Kasniji odjeljci usredotočit će se na hvatanje iznimaka i čišćenje nakon njih, ali prvo naučimo više o mogućnostima bacanja.

Izjava o bacanju

Java pruža throwizjavu za bacanje objekta koji opisuje iznimku. Evo sintakse throwizjave:

throw throwable;

Objekt koji je identificiran throwableje instanca Throwableili bilo koja od njegovih potklasa. Međutim, obično bacate samo predmete instancirane iz podrazreda Exceptionili RuntimeException. Evo nekoliko primjera:

throw new FileNotFoundException("unable to find file " + filename); throw new IllegalArgumentException("argument passed to count is less than zero");

Mogućnost bacanja baca se iz trenutne metode u JVM, koji provjerava odgovarajući rukovatelj ovom metodom. Ako nije pronađen, JVM odmotava skup poziva-metoda, tražeći najbližu metodu pozivanja koja se može nositi s iznimkom koju opisuje bacanje. Ako pronađe ovu metodu, prenosi mogućnost bacanja na obrađivač metode čiji se kôd izvršava za obradu iznimke. Ako se ne pronađe metoda koja bi obradila iznimku, JVM završava s odgovarajućom porukom.

Klauzula o bacanju

You need to inform the compiler when you throw a checked exception out of a method. Do this by appending a throws clause to the method's header. This clause has the following syntax:

throws checkedExceptionClassName (, checkedExceptionClassName)*

A throws clause consists of keyword throws followed by a comma-separated list of the class names of checked exceptions thrown out of the method. Here is an example:

public static void main(String[] args) throws ClassNotFoundException { if (args.length != 1) { System.err.println("usage: java ... classfile"); return; } Class.forName(args[0]); }

This example attempts to load a classfile identified by a command-line argument. If Class.forName() cannot find the classfile, it throws a java.lang.ClassNotFoundException object, which is a checked exception.

Checked exception controversy

The throws clause and checked exceptions are controversial. Many developers hate being forced to specify throws or handle the checked exception(s). Learn more about this from my Are checked exceptions good or bad? blog post.