1. Typer unntak

Typer unntak

Alle unntak er delt inn i 4 typer, som faktisk er klasser som arver hverandre.

Throwableklasse

Basisklassen for alle unntak er Throwableklassen. Klassen Throwableinneholder koden som skriver gjeldende anropsstabel (stabelsporing av gjeldende metode) til en matrise. Vi får vite hva en stabelsporing er litt senere.

Kast - operatøren kan bare godta et objekt som stammer fra Throwableklassen. Og selv om du teoretisk kan skrive kode som throw new Throwable();, er det vanligvis ingen som gjør dette. Hovedformålet med Throwableklassen er å ha en enkeltforelderklasse for alle unntak.

Errorklasse

Den neste unntaksklassen er Errorklassen, som direkte arver Throwableklassen. Java-maskinen lager objekter av Errorklassen (og dens etterkommere) når det har oppstått alvorlige problemer . For eksempel en maskinvarefeil, utilstrekkelig minne, etc.

Vanligvis, som programmerer, er det ingenting du kan gjøre i en situasjon der en slik feil (den typen en Errorbør kastes for) har oppstått i programmet: disse feilene er for alvorlige. Alt du kan gjøre er å varsle brukeren om at programmet krasjer og/eller skrive all kjent informasjon om feilen til programloggen.

Exceptionklasse

Klassene Exceptionog RuntimeExceptioner for vanlige feil som skjer i driften av mange metoder. Målet med hvert kastet unntak er å bli fanget av en catchblokk som vet hvordan den skal håndteres på riktig måte.

Når en metode av en eller annen grunn ikke kan fullføre arbeidet, bør den umiddelbart varsle kallemetoden ved å kaste et unntak av den aktuelle typen.

Med andre ord, hvis en variabel er lik null, vil metoden kaste en NullPointerException. Hvis de uriktige argumentene ble sendt til metoden, vil den kaste en InvalidArgumentException. Hvis metoden ved et uhell deler på null, vil den kaste en ArithmeticException.

RuntimeExceptionklasse

RuntimeExceptionser en undergruppe av Exceptions. Vi kan til og med si at RuntimeExceptiondet er en lett versjon av vanlige unntak ( Exception) — færre krav og begrensninger er pålagt slike unntak

Du vil lære forskjellen mellom Exceptionog RuntimeExceptionsenere.


2. Throws: sjekket unntak

Kast: sjekket unntak

Alle Java-unntak faller inn i 2 kategorier: avmerket og uavmerket .

Alle unntak som arver RuntimeExceptioneller Errorregnes som ukontrollerte unntak . Alle andre er sjekkede unntak .

Viktig!

Tjue år etter at sjekkede unntak ble introdusert, ser nesten alle Java-programmerere på dette som en feil. I populære moderne rammeverk er 95 % av alle unntak umerket. C#-språket, som nesten kopierte Java nøyaktig, la ikke til sjekkede unntak .

Hva er hovedforskjellen mellom sjekkede og ukontrollerte unntak?

Det stilles tilleggskrav til sjekkede unntak. Grovt sett er de disse:

Krav 1

Hvis en metode kaster et sjekket unntak , må den angi typen unntak i signaturen . På den måten er hver metode som kaller den klar over at dette "meningsfulle unntaket" kan forekomme i den.

Angi avmerkede unntak etter metodeparameterne etter throwsnøkkelordet (ikke bruk thrownøkkelordet ved en feiltakelse). Det ser omtrent slik ut:

type method (parameters) throws exception

Eksempel:

sjekket unntak uavmerket unntak
public void calculate(int n) throws Exception
{
   if (n == 0)
      throw new Exception("n is null!");
}
public void calculate(n)
{
   if (n == 0)
      throw new RuntimeException("n is null!");
}

I eksemplet til høyre gir koden vår et ukontrollert unntak - ingen ekstra handling er nødvendig. I eksempelet til venstre kaster metoden et avmerket unntak, så throwsnøkkelordet legges til metodesignaturen sammen med typen unntak.

Hvis en metode forventer å gi flere avmerkede unntak, må alle spesifiseres etter throwsnøkkelordet, atskilt med komma. Rekkefølgen er ikke viktig. Eksempel:

public void calculate(int n) throws Exception, IOException
{
   if (n == 0)
      throw new Exception("n is null!");
   if (n == 1)
      throw new IOException("n is 1");
}

Krav 2

Hvis du kaller en metode som har sjekket unntak i signaturen, kan du ikke se bort fra det faktum at den kaster dem.

Du må enten fange opp alle slike unntak ved å legge til catchblokker for hver enkelt, eller ved å legge dem til en throwsklausul for metoden din.

Det er som om vi sier: " Disse unntakene er så viktige at vi må fange dem. Og hvis vi ikke vet hvordan vi skal håndtere dem, så må alle som kan ringe metoden vår få beskjed om at slike unntak kan forekomme i den.

Eksempel:

Tenk deg at vi skriver en metode for å skape en verden befolket av mennesker. Det opprinnelige antallet personer sendes som et argument. Så vi må legge til unntak hvis det er for få personer.

Skaper jorden Merk
public void createWorld(int n) throws EmptyWorldException, LonelyWorldException
{
   if (n == 0)
      throw new EmptyWorldException("There are no people!");
   if (n == 1)
      throw new LonelyWorldException ("There aren't enough people!");
   System.out.println("A wonderful world was created. Population: " + n);
}
Metoden kaster potensielt to sjekkede unntak:

  • EmptyWorldException
  • LonelyWorldException

Dette metodekallet kan håndteres på 3 måter:

1. Ikke ta noen unntak

Dette gjøres oftest når metoden ikke vet hvordan den skal håndtere situasjonen på riktig måte.

Kode Merk
public void createPopulatedWorld(int population)
throws EmptyWorldException, LonelyWorldException
{
   createWorld(population);
}
Anropsmetoden fanger ikke opp unntakene og må informere andre om dem: den legger dem til sin egen throwsklausul

2. Fang noen av unntakene

Vi håndterer feilene vi kan håndtere. Men de vi ikke forstår, kaster vi dem opp til kallemetoden. For å gjøre dette, må vi legge til navnet deres i throws-klausulen:

Kode Merk
public void createNonEmptyWorld(int population)
throws EmptyWorldException
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
}
Den som ringer fanger bare ett avkrysset unntak — LonelyWorldException. Det andre unntaket må legges til signaturen, og angi det etter throwsnøkkelordet

3. Fang alle unntak

Hvis metoden ikke gir unntak fra anropsmetoden, er anropsmetoden alltid sikker på at alt fungerte bra. Og det vil ikke være i stand til å iverksette tiltak for å fikse en eksepsjonell situasjon.

Kode Merk
public void createAnyWorld(int population)
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
   catch (EmptyWorldException e)
   {
      e.printStackTrace();
   }
}
Alle unntak er fanget opp i denne metoden. Den som ringer vil være trygg på at alt gikk bra.


3. Unntak for innpakning

Sjekkede unntak virket kult i teorien, men viste seg å være en enorm frustrasjon i praksis.

Anta at du har en superpopulær metode i prosjektet ditt. Det kalles fra hundrevis av steder i programmet ditt. Og du bestemmer deg for å legge til et nytt avmerket unntak til det. Og det kan godt være at dette sjekkede unntaket er veldig viktig og så spesielt at bare main()metoden vet hva den skal gjøre hvis den blir fanget.

Det betyr at du må legge til det merkede unntaket til throwsklausulen for hver metode som kaller din superpopulære metode . Samt i throwsklausulen av alle metodene som kaller disse metodene. Og av metodene som kaller disse metodene.

Som et resultat throwsfår klausulene til halvparten av metodene i prosjektet et nytt avkrysset unntak. Og selvfølgelig er prosjektet ditt dekket av tester, og nå kompileres ikke testene. Og nå må du redigere kast-klausulene i testene dine også.

Og da må all koden din (alle endringene i hundrevis av filer) gjennomgås av andre programmerere. Og på dette tidspunktet spør vi oss selv hvorfor vi gjorde så mange blodige endringer i prosjektet? Arbeidsdag(er?) og ødelagte tester - alt for å legge til ett sjekket unntak?

Og selvfølgelig er det fortsatt problemer knyttet til arv og metodeoverstyring. Problemene som kommer fra sjekkede unntak er mye større enn nytten. Poenget er at nå er det få som elsker dem og få bruker dem.

Imidlertid er det fortsatt mye kode (inkludert standard Java-bibliotekkode) som inneholder disse avmerkede unntakene. Hva skal gjøres med dem? Vi kan ikke ignorere dem, og vi vet ikke hvordan vi skal håndtere dem.

Java-programmerere foreslo å legge inn sjekkede unntak i RuntimeException. Med andre ord, fange opp alle de avmerkede unntakene og deretter opprette ukontrollerte unntak (for eksempel RuntimeException) og kaste dem i stedet. Å gjøre det ser omtrent slik ut:

try
{
   // Code where a checked exception might occur
}
catch(Exception exp)
{
   throw new RuntimeException(exp);
}

Det er ikke en veldig pen løsning, men det er ikke noe kriminelt her: unntaket var rett og slett stappet i en RuntimeException.

Om ønskelig kan du enkelt hente den derfra. Eksempel:

Kode Merk
try
{
   // Code where we wrap the checked exception
   // in a RuntimeException
}
catch(RuntimeException e)
{
   Throwable cause = e.getCause();
   if (cause instanceof Exception)
   {
      Exception exp = (Exception) cause;
      // Exception handling code goes here
   }
}







Få unntaket lagret inne i RuntimeExceptionobjektet. Variabelen causekan kanskje null

bestemme typen og konvertere den til en avkrysset unntakstype.


4. Fange flere unntak

Programmerere hater virkelig å duplisere kode. De kom til og med opp med et tilsvarende utviklingsprinsipp: Don't Repeat Yourself (DRY) . Men når du håndterer unntak, er det hyppige tilfeller der en tryblokk blir fulgt av flere catchblokker med samme kode.

Eller det kan være 3 catchblokker med samme kode og ytterligere 2 catchblokker med annen identisk kode. Dette er en standardsituasjon når prosjektet ditt håndterer unntak på en ansvarlig måte.

Fra og med versjon 7, i Java-språket lagt til muligheten til å spesifisere flere typer unntak i en enkelt catchblokk. Det ser omtrent slik ut:

try
{
   // Code where an exception might occur
}
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 name)
{
   // Exception handling code
}

Du kan ha så mange catchblokker du vil. En enkelt blokk kan imidlertid catchikke spesifisere unntak som arver hverandre. Du kan med andre ord ikke skrive catch ( Exception| RuntimeExceptione), fordi RuntimeExceptionklassen arver Exception.



5. Egendefinerte unntak

Du kan alltid lage din egen unntaksklasse. Du oppretter ganske enkelt en klasse som arver RuntimeExceptionklassen. Det vil se omtrent slik ut:

class ClassName extends RuntimeException
{
}

Vi vil diskutere detaljene mens du lærer OOP, arv, konstruktører og metodeoverstyring.

Men selv om du bare har en enkel klasse som denne (helt uten kode), kan du fortsatt kaste unntak basert på den:

Kode Merk
class Solution
{
   public static void main(String[] args)
   {
      throw new MyException();
   }
}

class MyException extends RuntimeException
{
}




Kast en umerket MyException .

I Java Multithreading- oppdraget vil vi ta et dypdykk i arbeidet med våre egne tilpassede unntak.