1. Typer af undtagelser

Typer af undtagelser

Alle undtagelser er opdelt i 4 typer, som faktisk er klasser, der arver hinanden.

Throwableklasse

Basisklassen for alle undtagelser er Throwableklassen. Klassen Throwableindeholder koden, der skriver den aktuelle opkaldsstak (staksporing af den aktuelle metode) til et array. Vi lærer, hvad et stakspor er lidt senere.

Kasteoperatøren kan kun acceptere et objekt, der stammer fra klassenThrowable . Og selvom du teoretisk kan skrive kode som throw new Throwable();, er der normalt ingen, der gør dette. Klassens hovedformål Throwableer at have en enkeltforælderklasse for alle undtagelser.

Errorklasse

Den næste undtagelsesklasse er Errorklassen, som direkte arver Throwableklassen. Java-maskinen opretter objekter af Errorklassen (og dens efterkommere), når der er opstået alvorlige problemer . For eksempel en hardwarefejl, utilstrækkelig hukommelse osv.

Normalt, som programmør, er der ikke noget, du kan gøre i en situation, hvor en sådan fejl (den slags, som en Errorskal kastes for) er opstået i programmet: disse fejl er for alvorlige. Det eneste du kan gøre er at give brugeren besked om, at programmet går ned og/eller skrive alle kendte oplysninger om fejlen til programloggen.

Exceptionklasse

Klasserne Exceptionog RuntimeExceptioner til almindelige fejl, der opstår i driften af ​​mange metoder. Målet med hver kastet undtagelse er at blive fanget af en catchblok, der ved, hvordan man håndterer den korrekt.

Når en metode af en eller anden grund ikke kan fuldføre sit arbejde, skal den straks underrette den kaldende metode ved at kaste en undtagelse af den passende type.

Med andre ord, hvis en variabel er lig med null, vil metoden kaste en NullPointerException. Hvis de forkerte argumenter blev videregivet til metoden, vil den kaste en InvalidArgumentException. Hvis metoden ved et uheld deler sig med nul, vil den kaste en ArithmeticException.

RuntimeExceptionklasse

RuntimeExceptionser en delmængde af Exceptions. Vi kunne endda sige, at RuntimeExceptiondet er en letvægtsversion af almindelige undtagelser ( Exception) — færre krav og begrænsninger er pålagt sådanne undtagelser

Du vil lære forskellen mellem Exceptionog RuntimeExceptionsenere.


2. Throws: kontrollerede undtagelser

Kaster: kontrollerede undtagelser

Alle Java-undtagelser falder i 2 kategorier: markeret og umarkeret .

Alle undtagelser, der arver RuntimeExceptioneller Errorbetragtes som ukontrollerede undtagelser . Alle andre er kontrollerede undtagelser .

Vigtig!

Tyve år efter at kontrollerede undtagelser blev introduceret, opfatter næsten enhver Java-programmør dette som en fejl. I populære moderne rammer er 95% af alle undtagelser ikke markeret. C#-sproget, som næsten kopierede Java nøjagtigt, tilføjede ikke kontrollerede undtagelser .

Hvad er hovedforskellen mellem kontrollerede og ukontrollerede undtagelser?

Der stilles yderligere krav til kontrollerede undtagelser. Groft sagt er de disse:

Krav 1

Hvis en metode kaster en markeret undtagelse , skal den angive typen af ​​undtagelse i sin signatur . På den måde er enhver metode, der kalder den, klar over, at denne "meningsfulde undtagelse" kan forekomme i den.

Angiv markerede undtagelser efter metodeparametrene efter throwsnøgleordet (brug ikke thrownøgleordet ved en fejl). Det ser sådan ud:

type method (parameters) throws exception

Eksempel:

kontrolleret undtagelse umarkeret undtagelse
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øjre giver vores kode en umarkeret undtagelse - ingen yderligere handling er påkrævet. I eksemplet til venstre kaster metoden en markeret undtagelse, så throwsnøgleordet tilføjes til metodesignaturen sammen med typen af ​​undtagelsen.

Hvis en metode forventer at kaste flere markerede undtagelser, skal alle angives efter throwsnøgleordet, adskilt af kommaer. Rækkefølgen er ikke vigtig. 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 kalder en metode, der har kontrolleret undtagelser i sin signatur, kan du ikke ignorere, at den kaster dem.

Du skal enten fange alle sådanne undtagelser ved at tilføje catchblokke for hver enkelt, eller ved at tilføje dem til en throwsklausul for din metode.

Det er, som om vi siger: " Disse undtagelser er så vigtige, at vi må fange dem. Og hvis vi ikke ved, hvordan vi skal håndtere dem, så skal enhver, der måtte kalde vores metode, have besked om, at sådanne undtagelser kan forekomme i den.

Eksempel:

Forestil dig, at vi skriver en metode til at skabe en verden befolket af mennesker. Det oprindelige antal personer sendes som et argument. Så vi skal tilføje undtagelser, hvis der er for få mennesker.

At skabe Jorden Bemærk
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 potentielt to kontrollerede undtagelser:

  • EmptyWorldException
  • LonelyWorldException

Dette metodekald kan håndteres på 3 måder:

1. Fang ikke nogen undtagelser

Dette gøres oftest, når metoden ikke ved, hvordan man korrekt håndterer situationen.

Kode Bemærk
public void createPopulatedWorld(int population)
throws EmptyWorldException, LonelyWorldException
{
   createWorld(population);
}
Kaldmetoden fanger ikke undtagelserne og skal informere andre om dem: den føjer dem til sin egen throwsklausul

2. Fang nogle af undtagelserne

Vi håndterer de fejl, vi kan håndtere. Men dem, vi ikke forstår, kaster vi dem op til kaldemetoden. For at gøre dette skal vi tilføje deres navn til throws-klausulen:

Kode Bemærk
public void createNonEmptyWorld(int population)
throws EmptyWorldException
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
}
Den, der ringer, fanger kun én markeret undtagelse — LonelyWorldException. Den anden undtagelse skal føjes til sin signatur, hvilket angiver den efter throwsnøgleordet

3. Fang alle undtagelser

Hvis metoden ikke kaster undtagelser fra den kaldende metode, så er den kaldende metode altid sikker på, at alt fungerede godt. Og det vil ikke være i stand til at foretage sig noget for at rette op på en usædvanlig situation.

Kode Bemærk
public void createAnyWorld(int population)
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
   catch (EmptyWorldException e)
   {
      e.printStackTrace();
   }
}
Alle undtagelser er fanget i denne metode. Den, der ringer, vil være sikker på, at alt gik godt.


3. Indpakningsundtagelser

Tjekkede undtagelser virkede cool i teorien, men viste sig at være en kæmpe frustration i praksis.

Antag, at du har en super populær metode i dit projekt. Det kaldes fra hundredvis af steder i dit program. Og du beslutter dig for at tilføje en ny markeret undtagelse til den. Og det kan godt være, at denne afkrydsede undtagelse er rigtig vigtig og så speciel, at kun main()metoden ved, hvad den skal gøre, hvis den bliver fanget.

Det betyder , at du bliver nødt til at tilføje den markerede undtagelse til throwsklausulen for hver metode, der kalder din superpopulære metode . Samt i throwsklausulen af ​​alle de metoder, der kalder disse metoder. Og af de metoder, der kalder de metoder.

Som følge heraf throwsfår klausulerne i halvdelen af ​​metoderne i projektet en ny kontrolleret undtagelse. Og selvfølgelig er dit projekt dækket af test, og nu kompilerer testene ikke. Og nu skal du også redigere kast-klausulerne i dine tests.

Og så skal al din kode (alle ændringerne i hundredvis af filer) gennemgås af andre programmører. Og på dette tidspunkt spørger vi os selv, hvorfor vi lavede så mange blodige ændringer i projektet? Arbejdsdag(e?) og brudte tests - alt sammen for at tilføje en kontrolleret undtagelse?

Og selvfølgelig er der stadig problemer i forbindelse med arv og metodetilsidesættelse. Problemerne, der kommer fra kontrollerede undtagelser, er meget større end fordelen. Den nederste linje er, at nu er de færreste, der elsker dem, og få mennesker bruger dem.

Der er dog stadig en masse kode (inklusive standard Java-bibliotekskode), der indeholder disse kontrollerede undtagelser. Hvad skal der gøres med dem? Vi kan ikke ignorere dem, og vi ved ikke, hvordan vi skal håndtere dem.

Java-programmører foreslog at indpakke kontrollerede undtagelser i RuntimeException. Med andre ord, fange alle de afkrydsede undtagelser og derefter oprette umarkerede undtagelser (for eksempel RuntimeException) og smid dem i stedet for. At gøre det ser sådan her ud:

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

Det er ikke en særlig køn løsning, men der er ikke noget kriminelt her: undtagelsen var simpelthen proppet inde i en RuntimeException.

Hvis det ønskes, kan du nemt hente det derfra. Eksempel:

Kode Bemærk
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å undtagelsen gemt inde i RuntimeExceptionobjektet. Variablen causekan måske null

bestemme dens type og konvertere den til en markeret undtagelsestype.


4. Fange flere undtagelser

Programmører hader virkelig at duplikere kode. De kom endda med et tilsvarende udviklingsprincip: Don't Repeat Yourself (DRY) . Men når du håndterer undtagelser, er der hyppige lejligheder, hvor en tryblok efterfølges af flere catchblokke med samme kode.

Eller der kunne være 3 catchblokke med samme kode og yderligere 2 catchblokke med anden identisk kode. Dette er en standardsituation, når dit projekt håndterer undtagelser ansvarligt.

Startende med version 7 tilføjede Java-sproget muligheden for at specificere flere typer undtagelser i en enkelt catchblok. Det ser sådan ud:

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

Du kan have så mange catchblokke, som du vil. En enkelt blok kan dog catchikke specificere undtagelser, der arver hinanden. Du kan med andre ord ikke skrive catch ( Exception| RuntimeExceptione), fordi RuntimeExceptionklassen arver Exception.



5. Brugerdefinerede undtagelser

Du kan altid oprette din egen undtagelsesklasse. Du opretter blot en klasse, der arver RuntimeExceptionklassen. Det kommer til at se sådan ud:

class ClassName extends RuntimeException
{
}

Vi vil diskutere detaljerne, efterhånden som du lærer OOP, arv, konstruktører og metodetilsidesættelse.

Men selvom du kun har en simpel klasse som denne (helt uden kode), kan du stadig smide undtagelser baseret på den:

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

class MyException extends RuntimeException
{
}




Smid en umarkeret MyException .

I Java Multithreading- questen vil vi tage et dybt dyk ned i arbejdet med vores egne tilpassede undtagelser.