1. Soorten uitzonderingen

Soorten uitzonderingen

Alle uitzonderingen zijn onderverdeeld in 4 typen, die eigenlijk klassen zijn die elkaar overerven.

Throwableklas

De basisklasse voor alle uitzonderingen is de Throwableklasse. De Throwableklasse bevat de code die de huidige call-stack (stacktracering van de huidige methode) naar een array schrijft. We zullen later leren wat een stacktracering is.

De throw- operator kan alleen een object accepteren dat is afgeleid van de Throwableklasse. En hoewel je in theorie code kunt schrijven als throw new Throwable();, doet meestal niemand dit. Het belangrijkste doel van de Throwableklasse is om één enkele bovenliggende klasse te hebben voor alle uitzonderingen.

Errorklas

De volgende uitzonderingsklasse is de Errorklasse, die de klasse rechtstreeks overerft Throwable. De Java-machine maakt objecten van de Errorklasse (en zijn afstammelingen) wanneer er ernstige problemen zijn opgetreden . Bijvoorbeeld een hardwarestoring, onvoldoende geheugen, enz.

Gewoonlijk kun je als programmeur niets doen in een situatie waarin een dergelijke fout (het soort waarvoor een Errormoet worden gegenereerd) in het programma is opgetreden: deze fouten zijn te ernstig. Het enige wat u kunt doen is de gebruiker op de hoogte stellen dat het programma crasht en/of alle bekende informatie over de fout naar het programmalogboek schrijven.

Exceptionklas

De klassen Exceptionen RuntimeExceptionzijn voor veelvoorkomende fouten die optreden bij de werking van veel methoden. Het doel van elke gegooide uitzondering is om te worden opgevangen door een catchblok dat weet hoe het op de juiste manier moet worden afgehandeld.

Wanneer een methode om de een of andere reden zijn werk niet kan voltooien, moet deze de aanroepende methode onmiddellijk op de hoogte stellen door een uitzondering van het juiste type te genereren.

Met andere woorden, als een variabele gelijk is aan null, zal de methode een NullPointerException. Als de onjuiste argumenten aan de methode zijn doorgegeven, zal deze een InvalidArgumentException. Als de methode per ongeluk door nul deelt, wordt een ArithmeticException.

RuntimeExceptionklas

RuntimeExceptionszijn een subset van Exceptions. We zouden zelfs kunnen zeggen dat RuntimeExceptiondit een lichtgewicht versie is van gewone uitzonderingen ( Exception) — aan dergelijke uitzonderingen worden minder eisen en beperkingen gesteld

Je leert het verschil tussen Exceptionen RuntimeExceptionlater.


2. Throws: aangevinkte uitzonderingen

Gegooid: aangevinkte uitzonderingen

Alle Java-uitzonderingen vallen in 2 categorieën: aangevinkt en niet aangevinkt .

Alle uitzonderingen die de uitzonderingen erven RuntimeExceptionof Errorworden beschouwd als ongecontroleerde uitzonderingen . Alle andere zijn gecontroleerde uitzonderingen .

Belangrijk!

Twintig jaar nadat gecontroleerde uitzonderingen werden geïntroduceerd, beschouwt bijna elke Java-programmeur dit als een bug. In populaire moderne frameworks is 95% van alle uitzonderingen niet aangevinkt. De C#-taal, die bijna exact Java kopieerde, voegde geen gecontroleerde uitzonderingen toe .

Wat is het belangrijkste verschil tussen aangevinkte en ongecontroleerde uitzonderingen?

Aan aangevinkte uitzonderingen worden aanvullende eisen gesteld . Grofweg zijn dit deze:

Vereiste 1

Als een methode een aangevinkte uitzondering genereert , moet deze het type uitzondering aangeven in de handtekening . Op die manier weet elke methode die het aanroept dat deze "betekenisvolle uitzondering" erin kan voorkomen.

Geef aangevinkte uitzonderingen aan na de methodeparameters achter het throwstrefwoord (gebruik het trefwoord niet throwper ongeluk). Het ziet er ongeveer zo uit:

type method (parameters) throws exception

Voorbeeld:

aangevinkte uitzondering ongecontroleerde uitzondering
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!");
}

In het voorbeeld aan de rechterkant genereert onze code een ongecontroleerde uitzondering — er is geen aanvullende actie vereist. In het voorbeeld aan de linkerkant genereert de methode een aangevinkte uitzondering, dus het throwssleutelwoord wordt toegevoegd aan de handtekening van de methode, samen met het type uitzondering.

Als een methode meerdere aangevinkte uitzonderingen verwacht , moeten ze allemaal worden opgegeven na het throwstrefwoord, gescheiden door komma's. De volgorde is niet belangrijk. Voorbeeld:

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");
}

Vereiste 2

Als u een methode aanroept die uitzonderingen in zijn handtekening heeft gecontroleerd , kunt u niet negeren dat deze uitzonderingen genereert.

U moet al dergelijke uitzonderingen opvangen door catchvoor elke uitzondering blokken toe te voegen, of door ze toe te voegen aan een throwsclausule voor uw methode.

Het is alsof we zeggen: " Deze uitzonderingen zijn zo belangrijk dat we ze moeten opvangen. En als we niet weten hoe we ze moeten aanpakken, dan moet iedereen die onze methode aanroept, ervan op de hoogte worden gebracht dat dergelijke uitzonderingen erin kunnen voorkomen.

Voorbeeld:

Stel je voor dat we een methode schrijven om een ​​door mensen bevolkte wereld te creëren. Het aanvankelijke aantal mensen wordt als argument doorgegeven. We moeten dus uitzonderingen toevoegen als er te weinig mensen zijn.

Aarde creëren Opmerking
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);
}
De methode genereert mogelijk twee gecontroleerde uitzonderingen:

  • LegeWereldUitzondering
  • LonelyWorldException

Deze methodeaanroep kan op 3 manieren worden afgehandeld:

1. Vang geen uitzonderingen op

Dit wordt meestal gedaan wanneer de methode niet weet hoe ze de situatie goed moet aanpakken.

Code Opmerking
public void createPopulatedWorld(int population)
throws EmptyWorldException, LonelyWorldException
{
   createWorld(population);
}
De aanroepende methode vangt de uitzonderingen niet op en moet anderen erover informeren: het voegt ze toe aan zijn eigen throwsclausule

2. Vang enkele van de uitzonderingen op

Wij pakken de fouten aan die we aankunnen. Maar degenen die we niet begrijpen, gooien we over naar de aanroepmethode. Om dit te doen, moeten we hun naam toevoegen aan de throws-clausule:

Code Opmerking
public void createNonEmptyWorld(int population)
throws EmptyWorldException
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
}
De beller vangt slechts één aangevinkte uitzondering op — LonelyWorldException. De andere uitzondering moet aan de handtekening worden toegevoegd en wordt aangegeven na het throwstrefwoord

3. Vang alle uitzonderingen op

Als de methode geen uitzonderingen op de aanroepende methode genereert, heeft de aanroepende methode er altijd vertrouwen in dat alles goed werkte. En het zal geen actie kunnen ondernemen om uitzonderlijke situaties op te lossen.

Code Opmerking
public void createAnyWorld(int population)
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
   catch (EmptyWorldException e)
   {
      e.printStackTrace();
   }
}
Alle uitzonderingen worden in deze methode opgevangen. De beller weet zeker dat alles goed is gegaan.


3. Verpakkingsuitzonderingen

Gecontroleerde uitzonderingen leken in theorie cool, maar bleken in de praktijk een enorme frustratie.

Stel dat je een super populaire methode in je project hebt. Het wordt aangeroepen vanuit honderden plaatsen in uw programma. En u besluit er een nieuwe aangevinkte uitzondering aan toe te voegen. En het kan heel goed zijn dat deze aangevinkte exception echt belangrijk is en zo bijzonder dat alleen de main()methode weet wat te doen als hij wordt betrapt.

Dat betekent dat je de aangevinkte uitzondering moet toevoegen aan de clausule van elke methode die je super populaire methode aanroeptthrows . Evenals in de throwsclausule van alle methoden die deze methoden aanroepen. En van de methodes die die methodes aanroepen.

Als gevolg hiervan throwskrijgen de clausules van de helft van de methoden in het project een nieuwe aangevinkte uitzondering. En natuurlijk wordt uw project gedekt door tests, en nu worden de tests niet gecompileerd. En nu moet je ook de throws-clausules in je tests bewerken.

En dan moet al je code (alle wijzigingen in honderden bestanden) door andere programmeurs worden beoordeeld. En op dit punt vragen we ons af waarom we zoveel verdomde veranderingen in het project hebben aangebracht? Dag(en?) werk en mislukte tests - allemaal om één aangevinkte uitzondering toe te voegen?

En natuurlijk zijn er nog steeds problemen met overerving en methodeoverheersing. De problemen die voortkomen uit gecontroleerde uitzonderingen zijn veel groter dan het voordeel. Het komt erop neer dat nu maar weinig mensen van ze houden en dat maar weinig mensen ze gebruiken.

Er is echter nog steeds veel code (inclusief standaard Java-bibliotheekcode) die deze gecontroleerde uitzonderingen bevat. Wat moet er met hen gebeuren? We kunnen ze niet negeren en we weten niet hoe we ze moeten aanpakken.

Java-programmeurs stelden voor om aangevinkte uitzonderingen in RuntimeException. Met andere woorden, vang alle aangevinkte uitzonderingen op en maak vervolgens ongecontroleerde uitzonderingen (bijvoorbeeld RuntimeException) en gooi ze in plaats daarvan weg. Dat doen ziet er ongeveer zo uit:

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

Het is geen erg mooie oplossing, maar er is niets crimineels aan: de uitzondering zat gewoon in een RuntimeException.

Desgewenst kunt u deze daar eenvoudig weer ophalen. Voorbeeld:

Code Opmerking
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
   }
}







Haal de uitzondering op die in het RuntimeExceptionobject is opgeslagen. De causevariabele kan null

het type bepalen en converteren naar een gecontroleerd uitzonderingstype.


4. Meerdere uitzonderingen opvangen

Programmeurs hebben er echt een hekel aan om code te dupliceren. Ze bedachten zelfs een bijbehorend ontwikkelprincipe: Don't Repeat Yourself (DRY) . Maar bij het afhandelen van uitzonderingen komt het regelmatig voor dat een tryblok wordt gevolgd door meerdere catchblokken met dezelfde code.

Of er kunnen 3 catchblokken zijn met dezelfde code en nog eens 2 catchblokken met een andere identieke code. Dit is een standaardsituatie wanneer uw project verantwoord omgaat met uitzonderingen.

Vanaf versie 7 is in de Java-taal de mogelijkheid toegevoegd om meerdere soorten uitzonderingen in één catchblok op te geven. Het ziet er ongeveer zo uit:

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

Je kunt zoveel catchblokken hebben als je wilt. Een enkel blok kan echter catchgeen uitzonderingen specificeren die elkaar overerven. Met andere woorden, u kunt catch ( Exception| RuntimeExceptione) niet schrijven, omdat de RuntimeExceptionklasse overerft Exception.



5. Aangepaste uitzonderingen

U kunt altijd uw eigen uitzonderingsklasse maken. U maakt eenvoudig een klasse die de RuntimeExceptionklasse overerft. Het zal er ongeveer zo uitzien:

class ClassName extends RuntimeException
{
}

We bespreken de details terwijl u OOP, overerving, constructors en methode-overheersing leert.

Maar zelfs als je alleen een eenvoudige klasse als deze hebt (geheel zonder code), kun je nog steeds uitzonderingen maken op basis daarvan:

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

class MyException extends RuntimeException
{
}




Gooi een ongecontroleerd MyException .

In de Java Multithreading- zoektocht gaan we dieper in op het werken met onze eigen aangepaste uitzonderingen.