1. Typer av undantag

Alla undantag är indelade i 4 typer, som faktiskt är klasser som ärver varandra.

Throwableklass

Basklassen för alla undantag är Throwableklassen. Klassen Throwableinnehåller koden som skriver den aktuella anropsstacken (stackspårning av den aktuella metoden) till en array. Vi kommer att lära oss vad en stackspårning är lite senare.

Kastoperatören kan bara acceptera ett objekt som härrör från klassen . ThrowableOch även om du teoretiskt sett kan skriva kod som throw new Throwable();, brukar ingen göra detta. Huvudsyftet med Throwableklassen är att ha en ensamförälderklass för alla undantag.

Errorklass

Nästa undantagsklass är Errorklassen, som direkt ärver Throwableklassen. Java-maskinen skapar objekt av Errorklassen (och dess avkomlingar) när allvarliga problem har uppstått . Till exempel ett hårdvarufel, otillräckligt minne, etc.

Vanligtvis, som programmerare, finns det inget du kan göra i en situation där ett sådant fel (den typ som en Errorska kastas för) har inträffat i programmet: dessa fel är för allvarliga. Allt du kan göra är att meddela användaren att programmet kraschar och/eller skriva all känd information om felet till programloggen.

Exceptionklass

Klasserna Exceptionoch RuntimeExceptionär för vanliga fel som inträffar i driften av många metoder. Målet med varje kastat undantag är att fångas av ett catchblock som vet hur det ska hanteras på rätt sätt.

När en metod av någon anledning inte kan slutföra sitt arbete, bör den omedelbart meddela anropsmetoden genom att kasta ett undantag av lämplig typ.

Med andra ord, om en variabel är lika med null, kommer metoden att kasta en NullPointerException. Om de felaktiga argumenten skickades till metoden kommer den att kasta en InvalidArgumentException. Om metoden av misstag delar sig med noll, kommer den att kasta en ArithmeticException.

RuntimeExceptionklass

RuntimeExceptionsär en delmängd av Exceptions. Vi skulle till och med kunna säga att det RuntimeExceptionär en lätt version av vanliga undantag ( Exception) — färre krav och begränsningar ställs på sådana undantag

Du kommer att lära dig skillnaden mellan Exceptionoch RuntimeExceptionsenare.


2. Throws: markerade undantag

Alla Java-undantag delas in i två kategorier: markerad och omarkerad .

Alla undantag som ärver RuntimeExceptioneller Erroranses vara okontrollerade undantag . Alla andra är markerade undantag .

Viktig!

Tjugo år efter att kontrollerade undantag infördes, ser nästan alla Java-programmerare på detta som en bugg. I populära moderna ramverk är 95 % av alla undantag avmarkerade. C#-språket, som nästan kopierade Java exakt, lade inte till markerade undantag .

Vad är den största skillnaden mellan markerade och omarkerade undantag?

Det finns ytterligare krav på kontrollerade undantag. Grovt sett är de dessa:

Krav 1

Om en metod kastar ett markerat undantag måste den ange typen av undantag i sin signatur . På så sätt är varje metod som anropar den medveten om att detta "meningsfulla undantag" kan förekomma i den.

Ange markerade undantag efter metodparametrarna efter throwsnyckelordet (använd inte thrownyckelordet av misstag). Det ser ut ungefär så här:

type method (parameters) throws exception

Exempel:

kontrollerat undantag okontrollerat undantag
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 exemplet till höger ger vår kod ett omarkerat undantag – ingen ytterligare åtgärd krävs. I exemplet till vänster ger metoden ett markerat undantag, så throwsnyckelordet läggs till i metodsignaturen tillsammans med typen av undantag.

Om en metod förväntar sig att skapa flera markerade undantag måste alla anges efter throwsnyckelordet, avgränsade med kommatecken. Ordningen är inte viktig. Exempel:

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

Om du anropar en metod som har kontrollerat undantag i sin signatur kan du inte bortse från att den kastar dem.

Du måste antingen fånga alla sådana undantag genom att lägga till catchblock för var och en, eller genom att lägga till dem i en throwssats för din metod.

Det är som om vi säger: " Dessa undantag är så viktiga att vi måste fånga dem. Och om vi inte vet hur vi ska hantera dem, då måste alla som kan anropa vår metod meddelas att sådana undantag kan förekomma i den.

Exempel:

Föreställ dig att vi skriver en metod för att skapa en värld befolkad av människor. Det initiala antalet personer skickas som ett argument. Så vi måste lägga till undantag om det är för få personer.

Skapar jorden Notera
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 ger potentiellt två markerade undantag:

  • EmptyWorldException
  • LonelyWorldException

Detta metodanrop kan hanteras på tre sätt:

1. Fånga inga undantag

Detta görs oftast när metoden inte vet hur den ska hantera situationen på rätt sätt.

Koda Notera
public void createPopulatedWorld(int population)
throws EmptyWorldException, LonelyWorldException
{
   createWorld(population);
}
Anropsmetoden fångar inte upp undantagen och måste informera andra om dem: den lägger till dem i sin egen throwsklausul

2. Fånga några av undantagen

Vi hanterar de fel vi kan hantera. Men de vi inte förstår, vi kastar upp dem till anropsmetoden. För att göra detta måste vi lägga till deras namn till throws-satsen:

Koda Notera
public void createNonEmptyWorld(int population)
throws EmptyWorldException
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
}
Den som ringer fångar bara ett markerat undantag — LonelyWorldException. Det andra undantaget måste läggas till i sin signatur, vilket anger det efter throwsnyckelordet

3. Fånga alla undantag

Om metoden inte ger undantag från anropsmetoden, är anropsmetoden alltid säker på att allt fungerade bra. Och det kommer inte att kunna vidta några åtgärder för att fixa en exceptionell situation.

Koda Notera
public void createAnyWorld(int population)
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
   catch (EmptyWorldException e)
   {
      e.printStackTrace();
   }
}
Alla undantag fångas i denna metod. Den som ringer kommer att vara säker på att allt gick bra.


3. Fånga flera undantag

Programmerare hatar verkligen att duplicera kod. De kom till och med på en motsvarande utvecklingsprincip — DRY : Don't Repeat Yourself. Men vid hantering av undantag finns det ofta tillfällen då ett tryblock följs av flera catchblock med samma kod.

Eller det kan finnas 3 catchblock med samma kod och ytterligare 2 catchblock med annan identisk kod. Detta är en standardsituation när ditt projekt hanterar undantag på ett ansvarsfullt sätt.

Från och med version 7 lades i Java-språket till möjligheten att specificera flera typer av undantag i ett enda catchblock. Det ser ungefär ut så här:

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

Du kan ha hur många catchblock du vill. Ett enda catchblock kan dock inte specificera undantag som ärver varandra. Du kan med andra ord inte skriva catch ( Exception| RuntimeExceptione), eftersom RuntimeExceptionklassen ärver Exception.